From 14791d5a0e082983afcaf7ffa150faabf5931924 Mon Sep 17 00:00:00 2001 From: Mike Matera Date: Tue, 27 Jul 2021 12:40:13 -0700 Subject: [PATCH 001/898] Document Microk8s setup. --- .../step-zero-microk8s.md | 173 ++++++++++++++++++ doc/source/kubernetes/setup-kubernetes.md | 1 + 2 files changed, 174 insertions(+) create mode 100644 doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md new file mode 100644 index 0000000000..7714641f90 --- /dev/null +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -0,0 +1,173 @@ +(baremetal-microk8s)= + +# Kubernetes on a Bare Metal Host with MicroK8s + +If you have server hardware available and a small enough user base it's possible to use [Canonical's MicroK8s](https://microk8s.io/) in place of a cloud vendor. + +```{warning} +With no ability to scale users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. +``` + +This guide describes how to configure MicroK8s to work with Zero to Juptyerhub for Kubernetes. + +## Procedure + +1. Install MicroK8s as described in the Getting Started section of the [MicroK8s documentation](https://microk8s.io/docs). + + ```{note} + For production use consider installing MicroK8s on Ubuntu. Other platforms use an Ubuntu VM which reduces the available resources and performance. + ``` + +1. Enable the necessary MicroK8s Add ons: + + - [dns](https://microk8s.io/docs/addon-dns) + - helm3 + + ``` + microk8s enable dns + microk8s enable helm3 + ``` + +1. Configure networking. + + The Zero to JupyterHub helm chart defaults to using the `LoadBalancer` service type. On cloud vendors this triggers the allocation of a load balancer and IP address. In order for a `LoadBalancer` resource to work in MicroK8s the [MetalLB add on](https://microk8s.io/docs/addon-metallb) has to be enabled. + + MetalLB has two modes: [Layer 2 Mode](https://metallb.universe.tf/concepts/layer2/), the default and recommended mode, and [BGP Mode](https://metallb.universe.tf/concepts/bgp/). In Layer 2 mode, MetalLB needs a range of IP addresses that are on the same network as the host running MicroK8s. If the host has multiple interfaces you can choose addresses from of *any* of the interfaces. The range you give MetalLB can have as few as one IP address. When a `LoadBalancer` resource is requested MetalLB automatically adds one of its IP addresses to the interface of your host and passes the traffic into your Kubernetes system. This example shows how to enable a pool of addresses from `10.0.0.100` to `10.0.0.200`: + + ``` + microk8s enable metallb:10.0.0.100-200 + ``` + + If you give MetalLB a range of IP addresses you can choose one in your JupyterHub configuration. This is particularly important if you need TLS because you will have to setup a DNS entry for your server that has a fixed IP address. Here's an example proxy configuration with a fixed IP address request: + + ```yaml + ## Example config.yaml + proxy: + https: + enabled: true + hosts: + - jupyter.myschool.edu + letsencrypt: + contactEmail: me@myschool.edu + service: + loadBalancerIP: 10.0.0.150 + ``` + +1. Configure Storage. + + The JupyterHub chart uses persistent volume claims to allocate storage for notebooks and the hub database. Cloud vendors handle these claims automatically. On MicroK8s you have to enable the [OpenEBS Add-on](https://microk8s.io/docs/addon-openebs) so claims will be bound to storage. OpenEBS uses iSCSI for clustering which isn't necessary on a single host but the service must be enabled before you can enable OpenEBS: + + ``` + sudo systemctl enable iscsid.service + ``` + + Now you can enable OpenEBS: + + ``` + microk8s enable openebs + ``` + + OpenEBS installs a set of `StorageClass` resources but does not mark any of them default. Choose a directory on your host where you want to store data from your cluster. The path can be on the system disk or a separate disk. Create a YAML file called `local-storage-dir.yaml` with the following contents: + + ```yaml + ## local-storage-dir.yaml + apiVersion: storage.k8s.io/v1 + kind: StorageClass + metadata: + name: local-storage-dir + annotations: + storageclass.kubernetes.io/is-default-class: "true" + openebs.io/cas-type: local + cas.openebs.io/config: | + - name: StorageType + value: hostpath + - name: BasePath + value: /path/to/your/storage + provisioner: openebs.io/local + reclaimPolicy: Delete + volumeBindingMode: WaitForFirstConsumer + ``` + + ```{note} + Replace `/path/to/your/storage` with your desired path. + ``` + + Apply the customized `StorageClass` resource to your cluster: + + ``` + $ microk8s.kubectl apply -f local-storage-dir.yaml + ``` + + With a default storage class installed the Zero to JupyterHub chart doesn't require any customization. + +Now you're ready to install JupyterHub! + +## Resource Planning + +Hardware has to be sized for **peak load**. It's critical to plan for class size, especially if you intend to use JupyterHub in class with all attendees logged in simultaneously. Each running notebook server should be **guaranteed at least** 0.5 cores and 1G of RAM. Setting the limits lower will cause a lot of frustration. When a server exhausts its memory kernels die off and notebooks crash. The Kubernetes system and JupyterHub also consume resources that have to be accounted for. Assuming there are 2 cores and 2 GiB of RAM overhead this formula will tell you how to size a machine for a particular class size: + +```{math} +Cores = \left \lceil{2 + CPUGuarantee * ClassSize}\right \rceil + +RAM = 2 + RAMGuarantee * ClassSize +``` + +If you use the default limits in your configuration: + + +```yaml +## Example config.yaml +singleuser: + memory: + guarantee: 1G + cpu: + guarantee: 0.5 +``` + +and you have a class of 35 students that means you should have a machine with at least: + + +```{math} +Cores = \left \lceil{2 + 0.5 * 35}\right \rceil = 20 + +RAM = 2 + {1 * 35} = 37 +``` + +The default limits are fairly meager. If you intend to have your class doing real work or you expect that they will have many notebooks open simultaneously in JupyterLab you should plan for double the memory and CPU limits: + +```{math} +Cores = \left \lceil{2 + 1 * 35}\right \rceil = 37 + +RAM = 2 + {2 * 35} = 72 +``` + +These numbers are estimates. Disk usage and network bandwidth must also be considered. + + +## Troubleshooting + +1. The JupyterHub `LoadBalancer` resource is stuck in the `Pending` state. + + Verify that you have MetalLB installed correctly and that you gave it IP addresses. This command should show you two running pods. Check the controller pod log for errors: + + ``` + microk8s.kubectl -n metallb-system get all + ``` + +1. The hub pod is stuck pending `Pending` state. + + This is probably because the volume claim is unbound. Use this command to check: + + ``` + microk8s.kubectl get pvc + ``` + + If the claim is unbound verify that the storage class you created is correct and set to the default. + + ``` + microk8s.kubectl get sc + ``` + +1. Not everyone in my class can start the notebook. + + Kubernetes manages resources carefully. Each notebook server is guaranteed CPU and memory resources and when there are no more to give new servers cannot be scheduled. Read the Resource Planning section and adjust your limits accordingly. \ No newline at end of file diff --git a/doc/source/kubernetes/setup-kubernetes.md b/doc/source/kubernetes/setup-kubernetes.md index abb13b7325..d7f0d6d002 100644 --- a/doc/source/kubernetes/setup-kubernetes.md +++ b/doc/source/kubernetes/setup-kubernetes.md @@ -20,6 +20,7 @@ redhat/step-zero-openshift ibm/step-zero-ibm digital-ocean/step-zero-digital-ocean ovh/step-zero-ovh +other-infrastructure/step-zero-microk8s ``` ```{note} From a5ef3423bf54a73423d48c3316afdbbc169dd45d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Jul 2021 19:48:29 +0000 Subject: [PATCH 002/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../step-zero-microk8s.md | 225 +++++++++--------- 1 file changed, 111 insertions(+), 114 deletions(-) diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md index 7714641f90..c1e11f9bb8 100644 --- a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -2,109 +2,109 @@ # Kubernetes on a Bare Metal Host with MicroK8s -If you have server hardware available and a small enough user base it's possible to use [Canonical's MicroK8s](https://microk8s.io/) in place of a cloud vendor. +If you have server hardware available and a small enough user base it's possible to use [Canonical's MicroK8s](https://microk8s.io/) in place of a cloud vendor. ```{warning} -With no ability to scale users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. +With no ability to scale users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. ``` -This guide describes how to configure MicroK8s to work with Zero to Juptyerhub for Kubernetes. +This guide describes how to configure MicroK8s to work with Zero to Juptyerhub for Kubernetes. -## Procedure +## Procedure -1. Install MicroK8s as described in the Getting Started section of the [MicroK8s documentation](https://microk8s.io/docs). +1. Install MicroK8s as described in the Getting Started section of the [MicroK8s documentation](https://microk8s.io/docs). - ```{note} - For production use consider installing MicroK8s on Ubuntu. Other platforms use an Ubuntu VM which reduces the available resources and performance. - ``` + ```{note} + For production use consider installing MicroK8s on Ubuntu. Other platforms use an Ubuntu VM which reduces the available resources and performance. + ``` 1. Enable the necessary MicroK8s Add ons: - - [dns](https://microk8s.io/docs/addon-dns) - - helm3 - - ``` - microk8s enable dns - microk8s enable helm3 - ``` - -1. Configure networking. - - The Zero to JupyterHub helm chart defaults to using the `LoadBalancer` service type. On cloud vendors this triggers the allocation of a load balancer and IP address. In order for a `LoadBalancer` resource to work in MicroK8s the [MetalLB add on](https://microk8s.io/docs/addon-metallb) has to be enabled. - - MetalLB has two modes: [Layer 2 Mode](https://metallb.universe.tf/concepts/layer2/), the default and recommended mode, and [BGP Mode](https://metallb.universe.tf/concepts/bgp/). In Layer 2 mode, MetalLB needs a range of IP addresses that are on the same network as the host running MicroK8s. If the host has multiple interfaces you can choose addresses from of *any* of the interfaces. The range you give MetalLB can have as few as one IP address. When a `LoadBalancer` resource is requested MetalLB automatically adds one of its IP addresses to the interface of your host and passes the traffic into your Kubernetes system. This example shows how to enable a pool of addresses from `10.0.0.100` to `10.0.0.200`: - - ``` - microk8s enable metallb:10.0.0.100-200 - ``` - - If you give MetalLB a range of IP addresses you can choose one in your JupyterHub configuration. This is particularly important if you need TLS because you will have to setup a DNS entry for your server that has a fixed IP address. Here's an example proxy configuration with a fixed IP address request: - - ```yaml - ## Example config.yaml - proxy: - https: - enabled: true - hosts: - - jupyter.myschool.edu - letsencrypt: - contactEmail: me@myschool.edu - service: - loadBalancerIP: 10.0.0.150 - ``` - -1. Configure Storage. - - The JupyterHub chart uses persistent volume claims to allocate storage for notebooks and the hub database. Cloud vendors handle these claims automatically. On MicroK8s you have to enable the [OpenEBS Add-on](https://microk8s.io/docs/addon-openebs) so claims will be bound to storage. OpenEBS uses iSCSI for clustering which isn't necessary on a single host but the service must be enabled before you can enable OpenEBS: - - ``` - sudo systemctl enable iscsid.service - ``` - - Now you can enable OpenEBS: - - ``` - microk8s enable openebs - ``` - - OpenEBS installs a set of `StorageClass` resources but does not mark any of them default. Choose a directory on your host where you want to store data from your cluster. The path can be on the system disk or a separate disk. Create a YAML file called `local-storage-dir.yaml` with the following contents: - - ```yaml - ## local-storage-dir.yaml - apiVersion: storage.k8s.io/v1 - kind: StorageClass - metadata: - name: local-storage-dir - annotations: - storageclass.kubernetes.io/is-default-class: "true" - openebs.io/cas-type: local - cas.openebs.io/config: | - - name: StorageType - value: hostpath - - name: BasePath - value: /path/to/your/storage - provisioner: openebs.io/local - reclaimPolicy: Delete - volumeBindingMode: WaitForFirstConsumer - ``` - - ```{note} - Replace `/path/to/your/storage` with your desired path. - ``` - - Apply the customized `StorageClass` resource to your cluster: - - ``` - $ microk8s.kubectl apply -f local-storage-dir.yaml - ``` - - With a default storage class installed the Zero to JupyterHub chart doesn't require any customization. + - [dns](https://microk8s.io/docs/addon-dns) + - helm3 + + ``` + microk8s enable dns + microk8s enable helm3 + ``` + +1. Configure networking. + + The Zero to JupyterHub helm chart defaults to using the `LoadBalancer` service type. On cloud vendors this triggers the allocation of a load balancer and IP address. In order for a `LoadBalancer` resource to work in MicroK8s the [MetalLB add on](https://microk8s.io/docs/addon-metallb) has to be enabled. + + MetalLB has two modes: [Layer 2 Mode](https://metallb.universe.tf/concepts/layer2/), the default and recommended mode, and [BGP Mode](https://metallb.universe.tf/concepts/bgp/). In Layer 2 mode, MetalLB needs a range of IP addresses that are on the same network as the host running MicroK8s. If the host has multiple interfaces you can choose addresses from of _any_ of the interfaces. The range you give MetalLB can have as few as one IP address. When a `LoadBalancer` resource is requested MetalLB automatically adds one of its IP addresses to the interface of your host and passes the traffic into your Kubernetes system. This example shows how to enable a pool of addresses from `10.0.0.100` to `10.0.0.200`: + + ``` + microk8s enable metallb:10.0.0.100-200 + ``` + + If you give MetalLB a range of IP addresses you can choose one in your JupyterHub configuration. This is particularly important if you need TLS because you will have to setup a DNS entry for your server that has a fixed IP address. Here's an example proxy configuration with a fixed IP address request: + + ```yaml + ## Example config.yaml + proxy: + https: + enabled: true + hosts: + - jupyter.myschool.edu + letsencrypt: + contactEmail: me@myschool.edu + service: + loadBalancerIP: 10.0.0.150 + ``` + +1. Configure Storage. + + The JupyterHub chart uses persistent volume claims to allocate storage for notebooks and the hub database. Cloud vendors handle these claims automatically. On MicroK8s you have to enable the [OpenEBS Add-on](https://microk8s.io/docs/addon-openebs) so claims will be bound to storage. OpenEBS uses iSCSI for clustering which isn't necessary on a single host but the service must be enabled before you can enable OpenEBS: + + ``` + sudo systemctl enable iscsid.service + ``` + + Now you can enable OpenEBS: + + ``` + microk8s enable openebs + ``` + + OpenEBS installs a set of `StorageClass` resources but does not mark any of them default. Choose a directory on your host where you want to store data from your cluster. The path can be on the system disk or a separate disk. Create a YAML file called `local-storage-dir.yaml` with the following contents: + + ```yaml + ## local-storage-dir.yaml + apiVersion: storage.k8s.io/v1 + kind: StorageClass + metadata: + name: local-storage-dir + annotations: + storageclass.kubernetes.io/is-default-class: "true" + openebs.io/cas-type: local + cas.openebs.io/config: | + - name: StorageType + value: hostpath + - name: BasePath + value: /path/to/your/storage + provisioner: openebs.io/local + reclaimPolicy: Delete + volumeBindingMode: WaitForFirstConsumer + ``` + + ```{note} + Replace `/path/to/your/storage` with your desired path. + ``` + + Apply the customized `StorageClass` resource to your cluster: + + ``` + $ microk8s.kubectl apply -f local-storage-dir.yaml + ``` + + With a default storage class installed the Zero to JupyterHub chart doesn't require any customization. Now you're ready to install JupyterHub! - -## Resource Planning -Hardware has to be sized for **peak load**. It's critical to plan for class size, especially if you intend to use JupyterHub in class with all attendees logged in simultaneously. Each running notebook server should be **guaranteed at least** 0.5 cores and 1G of RAM. Setting the limits lower will cause a lot of frustration. When a server exhausts its memory kernels die off and notebooks crash. The Kubernetes system and JupyterHub also consume resources that have to be accounted for. Assuming there are 2 cores and 2 GiB of RAM overhead this formula will tell you how to size a machine for a particular class size: +## Resource Planning + +Hardware has to be sized for **peak load**. It's critical to plan for class size, especially if you intend to use JupyterHub in class with all attendees logged in simultaneously. Each running notebook server should be **guaranteed at least** 0.5 cores and 1G of RAM. Setting the limits lower will cause a lot of frustration. When a server exhausts its memory kernels die off and notebooks crash. The Kubernetes system and JupyterHub also consume resources that have to be accounted for. Assuming there are 2 cores and 2 GiB of RAM overhead this formula will tell you how to size a machine for a particular class size: ```{math} Cores = \left \lceil{2 + CPUGuarantee * ClassSize}\right \rceil @@ -114,8 +114,7 @@ RAM = 2 + RAMGuarantee * ClassSize If you use the default limits in your configuration: - -```yaml +```yaml ## Example config.yaml singleuser: memory: @@ -126,11 +125,10 @@ singleuser: and you have a class of 35 students that means you should have a machine with at least: - ```{math} Cores = \left \lceil{2 + 0.5 * 35}\right \rceil = 20 -RAM = 2 + {1 * 35} = 37 +RAM = 2 + {1 * 35} = 37 ``` The default limits are fairly meager. If you intend to have your class doing real work or you expect that they will have many notebooks open simultaneously in JupyterLab you should plan for double the memory and CPU limits: @@ -138,36 +136,35 @@ The default limits are fairly meager. If you intend to have your class doing rea ```{math} Cores = \left \lceil{2 + 1 * 35}\right \rceil = 37 -RAM = 2 + {2 * 35} = 72 +RAM = 2 + {2 * 35} = 72 ``` -These numbers are estimates. Disk usage and network bandwidth must also be considered. - +These numbers are estimates. Disk usage and network bandwidth must also be considered. -## Troubleshooting +## Troubleshooting -1. The JupyterHub `LoadBalancer` resource is stuck in the `Pending` state. +1. The JupyterHub `LoadBalancer` resource is stuck in the `Pending` state. - Verify that you have MetalLB installed correctly and that you gave it IP addresses. This command should show you two running pods. Check the controller pod log for errors: + Verify that you have MetalLB installed correctly and that you gave it IP addresses. This command should show you two running pods. Check the controller pod log for errors: - ``` - microk8s.kubectl -n metallb-system get all - ``` + ``` + microk8s.kubectl -n metallb-system get all + ``` -1. The hub pod is stuck pending `Pending` state. +1. The hub pod is stuck pending `Pending` state. - This is probably because the volume claim is unbound. Use this command to check: + This is probably because the volume claim is unbound. Use this command to check: - ``` - microk8s.kubectl get pvc - ``` + ``` + microk8s.kubectl get pvc + ``` - If the claim is unbound verify that the storage class you created is correct and set to the default. + If the claim is unbound verify that the storage class you created is correct and set to the default. - ``` - microk8s.kubectl get sc - ``` + ``` + microk8s.kubectl get sc + ``` -1. Not everyone in my class can start the notebook. +1. Not everyone in my class can start the notebook. - Kubernetes manages resources carefully. Each notebook server is guaranteed CPU and memory resources and when there are no more to give new servers cannot be scheduled. Read the Resource Planning section and adjust your limits accordingly. \ No newline at end of file + Kubernetes manages resources carefully. Each notebook server is guaranteed CPU and memory resources and when there are no more to give new servers cannot be scheduled. Read the Resource Planning section and adjust your limits accordingly. From e0265c13143bd8dee055b0e549af210e5d1dc5dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Sep 2021 00:19:30 +0000 Subject: [PATCH 003/898] build(deps): bump jupyterhub-ltiauthenticator in /images/hub Bumps [jupyterhub-ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) from 1.0.0 to 1.2.0. - [Release notes](https://github.com/jupyterhub/ltiauthenticator/releases) - [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/ltiauthenticator/compare/1.0.0...1.2.0) --- updated-dependencies: - dependency-name: jupyterhub-ltiauthenticator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3d08a2c65b..aec680c55a 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -35,7 +35,9 @@ cryptography==3.4.7 entrypoints==0.3 # via jupyterhub escapism==1.0.1 - # via jupyterhub-kubespawner + # via + # jupyterhub-kubespawner + # jupyterhub-ltiauthenticator google-auth==1.31.0 # via kubernetes greenlet==1.1.0 @@ -72,7 +74,7 @@ jupyterhub-kubespawner==1.1.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.0.0 +jupyterhub-ltiauthenticator==1.2.0 # via -r requirements.in jupyterhub-nativeauthenticator==0.0.7 # via -r requirements.in From 367c74feccde07fb64ba433300e5adc54562bdc9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Sep 2021 05:05:54 +0000 Subject: [PATCH 004/898] build(deps): bump nbgitpuller in /images/singleuser-sample Bumps [nbgitpuller](https://github.com/jupyterhub/nbgitpuller) from 0.10.2 to 1.0.0. - [Release notes](https://github.com/jupyterhub/nbgitpuller/releases) - [Changelog](https://github.com/jupyterhub/nbgitpuller/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nbgitpuller/compare/0.10.2...1.0.0) --- updated-dependencies: - dependency-name: nbgitpuller dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index bb666576df..ba507bb533 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -2,4 +2,4 @@ # .github/dependabot.yaml. jupyterhub==1.4.2 -nbgitpuller==0.10.2 +nbgitpuller==1.0.0 From ba1e667d22b262e0b873a79ebc17c50b76032860 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Sep 2021 05:05:37 +0000 Subject: [PATCH 005/898] build(deps): bump nbgitpuller in /images/singleuser-sample Bumps [nbgitpuller](https://github.com/jupyterhub/nbgitpuller) from 1.0.0 to 1.0.2. - [Release notes](https://github.com/jupyterhub/nbgitpuller/releases) - [Changelog](https://github.com/jupyterhub/nbgitpuller/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nbgitpuller/compare/1.0.0...1.0.2) --- updated-dependencies: - dependency-name: nbgitpuller dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index ba507bb533..37cff8b6ce 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -2,4 +2,4 @@ # .github/dependabot.yaml. jupyterhub==1.4.2 -nbgitpuller==1.0.0 +nbgitpuller==1.0.2 From e7a48e136de7aff5d8e26a070836485912d6a5fc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 4 Sep 2021 02:54:02 +0200 Subject: [PATCH 006/898] Remove no longer needed arm test adjustment --- dev-config-arm.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dev-config-arm.yaml b/dev-config-arm.yaml index 09140439b3..68629665a9 100644 --- a/dev-config-arm.yaml +++ b/dev-config-arm.yaml @@ -2,8 +2,3 @@ proxy: https: enabled: false - -singleuser: - image: - name: sakuraiyuta/base-notebook - tag: latest From 5f6de322ba27d43080f766d2124e7e284a92fe3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Sep 2021 05:04:38 +0000 Subject: [PATCH 007/898] build(deps): bump docker/setup-buildx-action from 1.5.1 to 1.6.0 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1.5.1 to 1.6.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/abe5d8f79a1606a2d3e218847032f3f2b1726ab0...94ab11c41e45d028884a99163086648e898eed25) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bf56d31a34..fe7002ec13 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -74,7 +74,7 @@ jobs: uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@abe5d8f79a1606a2d3e218847032f3f2b1726ab0 # dependabot updates to latest release + uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # dependabot updates to latest release - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 74d6c6e708..4e4ba16c2e 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -45,7 +45,7 @@ jobs: uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@abe5d8f79a1606a2d3e218847032f3f2b1726ab0 # dependabot updates to latest release + uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # dependabot updates to latest release - name: Build a multiple architecture Docker image run: >- From 60c5e9f86a529db25b87e4e605cce48e8e973872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Sep 2021 00:04:54 +0000 Subject: [PATCH 008/898] build(deps): bump py-spy from 0.3.8 to 0.3.9 in /images/hub Bumps [py-spy](https://github.com/benfred/py-spy) from 0.3.8 to 0.3.9. - [Release notes](https://github.com/benfred/py-spy/releases) - [Changelog](https://github.com/benfred/py-spy/blob/master/CHANGELOG.md) - [Commits](https://github.com/benfred/py-spy/compare/v0.3.8...v0.3.9) --- updated-dependencies: - dependency-name: py-spy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index aec680c55a..50552807a5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -110,7 +110,7 @@ prometheus-client==0.11.0 # via jupyterhub psycopg2-binary==2.9.1 # via -r requirements.in -py-spy==0.3.8 +py-spy==0.3.9 # via -r requirements.in pyasn1==0.4.8 # via From 465e705e9d3ba8b7af550832e0ef7b97501d2120 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Sep 2021 19:39:39 +0000 Subject: [PATCH 009/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v2.3.2 → v2.4.0](https://github.com/pre-commit/mirrors-prettier/compare/v2.3.2...v2.4.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f295bdb8f..6bc30c2870 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.3.2 + rev: v2.4.0 hooks: - id: prettier From 551ee6523e058b8f98742ddc88f7df37e4c3f967 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 14 Sep 2021 01:06:06 +0000 Subject: [PATCH 010/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 301dd68633..b42b039fa8 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-08-27_01:04:20 +# VULN_SCAN_TIME=2021-09-14_01:06:05 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From b317efa8fbea441a41e7893b0d2eac34e1a16e72 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 14 Sep 2021 01:06:42 +0000 Subject: [PATCH 011/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index deb64d652d..af6e3c8b46 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-08-27_01:05:08 +# VULN_SCAN_TIME=2021-09-14_01:06:40 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From e07af2900085340b5c91514e4ccc32d29ae01f92 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 16 Sep 2021 01:07:20 +0000 Subject: [PATCH 012/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index af6e3c8b46..88c810da39 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-09-14_01:06:40 +# VULN_SCAN_TIME=2021-09-16_01:07:18 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 33bec904c0691028e1e7742cde0891e364529915 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 17 Sep 2021 01:09:22 +0000 Subject: [PATCH 013/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index b42b039fa8..8efcf3b1b5 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-09-14_01:06:05 +# VULN_SCAN_TIME=2021-09-17_01:09:21 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 8cd5a1876f84aa266711d804dfa921b5894a0f35 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 17 Sep 2021 01:10:03 +0000 Subject: [PATCH 014/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 88c810da39..49e801468d 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-09-16_01:07:18 +# VULN_SCAN_TIME=2021-09-17_01:10:00 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 02262a2e246d230fa888544d6ac73110c99da6f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 16:02:29 +0000 Subject: [PATCH 015/898] build(deps): bump jupyterhub-idle-culler from 1.1 to 1.2 in /images/hub Bumps [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) from 1.1 to 1.2. - [Release notes](https://github.com/jupyterhub/jupyterhub-idle-culler/releases) - [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/master/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/jupyterhub-idle-culler/compare/v1.1...1.2) --- updated-dependencies: - dependency-name: jupyterhub-idle-culler dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 50552807a5..d3877d76d8 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -68,7 +68,7 @@ jupyterhub-firstuseauthenticator==0.14.1 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 # via -r requirements.in -jupyterhub-idle-culler==1.1 +jupyterhub-idle-culler==1.2 # via -r requirements.in jupyterhub-kubespawner==1.1.0 # via -r requirements.in From 9b5f89c5c2109888125f12cefcd0c551d5a1c66e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Sep 2021 19:59:10 +0000 Subject: [PATCH 016/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.8b0 → 21.9b0](https://github.com/psf/black/compare/21.8b0...21.9b0) - [github.com/pre-commit/mirrors-prettier: v2.4.0 → v2.4.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.4.0...v2.4.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6bc30c2870..116b085ab8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 21.8b0 + rev: 21.9b0 hooks: - id: black args: [--target-version=py36] @@ -34,7 +34,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.4.0 + rev: v2.4.1 hooks: - id: prettier From 2e2e981bc74c6d101dce9c2356a0ebe11673a89c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Sep 2021 03:51:15 +0200 Subject: [PATCH 017/898] pre-commit: add and run pyupgrade --- .pre-commit-config.yaml | 8 ++++++++ doc/source/conf.py | 8 ++++---- jupyterhub/files/hub/jupyterhub_config.py | 2 +- tests/conftest.py | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 116b085ab8..9ce7e92a25 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,6 +19,14 @@ # pre-commit run --config .pre-commit-config-shellcheck.yaml --all-files # repos: + # Autoformat: Python code, syntax patterns are modernized + - repo: https://github.com/asottile/pyupgrade + rev: v2.26.0 + hooks: + - id: pyupgrade + args: + - --py36-plus + # Autoformat: Python code - repo: https://github.com/psf/black rev: 21.9b0 diff --git a/doc/source/conf.py b/doc/source/conf.py index 7043d38e17..747748fba0 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -55,7 +55,7 @@ def _get_git_ref_from_chartpress_based_version(version): Get a git ref from a chartpress set version of format like 1.2.3-beta.1.n123.h1234567, 1.2.3-n123.h1234567, or 1.2.3. """ - tag_hash_split = re.split("[\.|-]n\d\d\d\.h", version) + tag_hash_split = re.split(r"[\.|-]n\d\d\d\.h", version) if len(tag_hash_split) == 2: return tag_hash_split[1] else: @@ -200,13 +200,13 @@ def _get_git_ref_from_chartpress_based_version(version): # -- Generate the Helm chart configuration reference from a schema file ------ # header -with open("resources/reference.txt", "r") as f: +with open("resources/reference.txt") as f: header_md = f.readlines() header_md = header_md[1:] header_md = [ln.strip("\n") for ln in header_md] # schema -with open("../../jupyterhub/schema.yaml", "r") as f: +with open("../../jupyterhub/schema.yaml") as f: data = yaml.safe_load(f) @@ -230,7 +230,7 @@ def parse_schema(d, md=[], depth=0, pre=""): md.append(ln) md.append("") - parse_schema(val, md, depth, pre + "{}.".format(key)) + parse_schema(val, md, depth, pre + f"{key}.") depth -= 1 return md diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index da0c2a4440..83cbe3fbdc 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -175,7 +175,7 @@ def camelCaseify(s): if image: tag = get_config("singleuser.image.tag") if tag: - image = "{}:{}".format(image, tag) + image = f"{image}:{tag}" c.KubeSpawner.image = image diff --git a/tests/conftest.py b/tests/conftest.py index c9d4fb8778..8f95660abe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -70,7 +70,7 @@ def pebble_acme_ca_cert(): return cert_path -class JupyterRequest(object): +class JupyterRequest: def __init__(self, request_data, pebble_acme_ca_cert): self.request_data = request_data self.pebble_acme_ca_cert = pebble_acme_ca_cert From b5d28ce261af74d70180a47d0a280123aea42023 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Sep 2021 04:16:27 +0200 Subject: [PATCH 018/898] Tighten permissions for jupyterhub-idle-culler --- jupyterhub/files/hub/jupyterhub_config.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index da0c2a4440..83daa162fd 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -323,8 +323,24 @@ def camelCaseify(s): ) c.JupyterHub.services = [] +c.JupyterHub.load_roles = [] +# jupyterhub-idle-culler's permissions are scoped to what it needs only, see +# https://github.com/jupyterhub/jupyterhub-idle-culler#permissions. +# if get_config("cull.enabled", False): + jupyterhub_idle_culler_role = { + "name": "jupyterhub-idle-culler", + "scopes": [ + "list:users", + "read:users:activity", + "servers", + # "admin:users", # dynamically added if --cull-users is passed + ], + # assign the role to a jupyterhub service, so it gains these permissions + "services": ["jupyterhub-idle-culler"], + } + cull_cmd = ["python3", "-m", "jupyterhub_idle_culler"] base_url = c.JupyterHub.get("base_url", "/") cull_cmd.append("--url=http://localhost:8081" + url_path_join(base_url, "hub/api")) @@ -343,6 +359,7 @@ def camelCaseify(s): if get_config("cull.users"): cull_cmd.append("--cull-users") + jupyterhub_idle_culler_role["scopes"].append("admin:users") if get_config("cull.removeNamedServers"): cull_cmd.append("--remove-named-servers") @@ -353,11 +370,11 @@ def camelCaseify(s): c.JupyterHub.services.append( { - "name": "cull-idle", - "admin": True, + "name": "jupyterhub-idle-culler", "command": cull_cmd, } ) + c.JupyterHub.load_roles.append(jupyterhub_idle_culler_role) for key, service in get_config("hub.services", {}).items(): # c.JupyterHub.services is a list of dicts, but From 9d7986d109172ba4a790e6896ea0ee2abeb324ce Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Sep 2021 04:22:00 +0200 Subject: [PATCH 019/898] Pin jupyterhub==2.0.0b1 and refreeze dependencies --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 90 ++++++++++++----------- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 6a6b4d1699..6ab7c008b1 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub +jupyterhub==2.0.0b1 ## Authenticators jupyterhub-firstuseauthenticator diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index d3877d76d8..4ae61a3049 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # ./dependencies freeze --upgrade # -alembic==1.6.5 +alembic==1.7.3 # via jupyterhub async-generator==1.10 # via @@ -24,13 +24,13 @@ certifi==2021.5.30 # requests certipy==0.1.3 # via jupyterhub -cffi==1.14.5 +cffi==1.14.6 # via # bcrypt # cryptography -chardet==4.0.0 +charset-normalizer==2.0.6 # via requests -cryptography==3.4.7 +cryptography==3.4.8 # via pyopenssl entrypoints==0.3 # via jupyterhub @@ -38,14 +38,14 @@ escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator -google-auth==1.31.0 +google-auth==2.1.0 # via kubernetes -greenlet==1.1.0 +greenlet==1.1.1 # via sqlalchemy -idna==2.10 +idna==3.2 # via requests -ipython-genutils==0.2.0 - # via traitlets +importlib-resources==5.2.2 + # via alembic jinja2==3.0.1 # via # jupyterhub @@ -54,16 +54,6 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==1.4.2 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator jupyterhub-firstuseauthenticator==0.14.1 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -80,11 +70,21 @@ jupyterhub-nativeauthenticator==0.0.7 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes==17.17.0 +jupyterhub==2.0.0b1 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator +kubernetes==18.20.0 # via jupyterhub-kubespawner -ldap3==2.9 +ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.1.4 +mako==1.1.5 # via alembic markupsafe==2.0.1 # via @@ -104,6 +104,8 @@ oauthlib==3.1.1 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator +packaging==21.0 + # via jupyterhub pamela==1.0.0 # via jupyterhub prometheus-client==0.11.0 @@ -112,13 +114,13 @@ psycopg2-binary==2.9.1 # via -r requirements.in py-spy==0.3.9 # via -r requirements.in +pyasn1-modules==0.2.8 + # via google-auth pyasn1==0.4.8 # via # ldap3 # pyasn1-modules # rsa -pyasn1-modules==0.2.8 - # via google-auth pycparser==2.20 # via cffi pycurl==7.44.1 @@ -131,17 +133,16 @@ pymysql==1.0.2 # via -r requirements.in pyopenssl==20.0.1 # via certipy -pyrsistent==0.17.3 +pyparsing==2.4.7 + # via packaging +pyrsistent==0.18.0 # via jsonschema -python-dateutil==2.8.1 +python-dateutil==2.8.2 # via - # alembic # jupyterhub # jupyterhub-idle-culler # kubernetes -python-editor==1.0.4 - # via alembic -python-json-logger==2.0.1 +python-json-logger==2.0.2 # via jupyter-telemetry python-slugify==5.0.2 # via jupyterhub-kubespawner @@ -149,39 +150,38 @@ pyyaml==5.4.1 # via # jupyterhub-kubespawner # kubernetes -requests==2.25.1 +requests-oauthlib==1.3.0 # via - # jupyterhub # kubernetes # mwoauth - # requests-oauthlib -requests-oauthlib==1.3.0 +requests==2.26.0 # via + # jupyterhub # kubernetes # mwoauth + # requests-oauthlib rsa==4.7.2 # via google-auth -ruamel.yaml==0.17.9 - # via jupyter-telemetry -ruamel.yaml.clib==0.2.2 +ruamel.yaml.clib==0.2.6 # via ruamel.yaml +ruamel.yaml==0.17.16 + # via jupyter-telemetry six==1.16.0 # via # bcrypt - # google-auth # jsonschema # kubernetes # mwoauth # onetimepass # pyopenssl # python-dateutil -sqlalchemy==1.4.18 +sqlalchemy-cockroachdb==1.4.0 + # via -r requirements.in +sqlalchemy==1.4.23 # via # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.0 - # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -191,18 +191,20 @@ tornado==6.1 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.0.5 +traitlets==5.1.0 # via # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.5 +urllib3==1.26.6 # via # jupyterhub-kubespawner # kubernetes # requests -websocket-client==1.1.0 +websocket-client==1.2.1 # via kubernetes +zipp==3.5.0 + # via importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 37cff8b6ce..7d757b15ef 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==1.4.2 +jupyterhub==2.0.0b1 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index fe73cbc118..6aae6c5d7c 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 1.4.2 +appVersion: 2.0.0b1 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 68c0c641bf68c3ab2028f45ff77e3eb8ae9cefce Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Sep 2021 18:27:39 +0200 Subject: [PATCH 020/898] Remove breaking change messages relevant for upgrading to 1.0.0 By removing these, we can simplify our schema.yaml file and various helm templates. The assumption is that users won't upgrade from z2jh `0.*` to `>=2` skipping `1.*`, if they would do that they would get errors that is less user friendly from the Helm schema validation logic. If you are upgrading to Z2JH 2+, first upgrade to Z2JH 1+ - don't go straight from `0.*`! --- jupyterhub/schema.yaml | 65 ----- jupyterhub/templates/NOTES.txt | 53 +---- jupyterhub/templates/_helpers-auth-rework.tpl | 223 ------------------ 3 files changed, 7 insertions(+), 334 deletions(-) delete mode 100644 jupyterhub/templates/_helpers-auth-rework.tpl diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 83af5b5ee9..724e7ec89c 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -870,22 +870,6 @@ properties: extra configuration logic. ``` - uid: - type: [integer, "null"] - minimum: 0 - deprecated: true - description: | - Deprecated since chart version 0.11.0. Use - [`hub.containerSecurityContext`](schema_hub.containerSecurityContext) - instead. - - The UID the hub process should be running as. - - Use this only if you are building your own image & know that a user with this uid - exists inside the hub container! Advanced feature, handle with care! - - Defaults to 1000, which is the uid of the `jovyan` user that is present in the - default hub image. fsGid: type: [integer, "null"] minimum: 0 @@ -1069,13 +1053,6 @@ properties: description: | JupyterHub does not support running in parallel, due to this we default to using a deployment strategy of Recreate. - extraConfigMap: - type: object - additionalProperties: true - deprecated: true - description: | - Deprecated since chart version 0.8. Use [`custom`](schema_custom) - instead. extraContainers: &extraContainers-spec type: array description: | @@ -1234,12 +1211,6 @@ properties: type: object additionalProperties: true description: *jupyterhub-native-config-description - imagePullSecret: &deprecated-imagePullSecret - type: object - additionalProperties: true - deprecated: true - description: | - DEPRECATED: Use [`imagePullSecret`](schema_imagePullSecret) instead. serviceAccount: &serviceAccount type: object additionalProperties: false @@ -1757,21 +1728,6 @@ properties: both receive updates from JupyterHub regarding how it should route traffic. Due to this we default to using a deployment strategy of Recreate instead of RollingUpdate. - containerSecurityContext: - type: object - additionalProperties: true - deprecated: true - description: | - Deprecated since chart version 0.11.0. Use - [`proxy.chp.containerSecurityContext`](schema_proxy.chp.containerSecurityContext) - instead. - networkPolicy: - type: object - additionalProperties: true - deprecated: true - description: | - Deprecated since chart version 0.11.0. Use - [`proxy.chp.networkPolicy`](schema_proxy.chp.networkPolicy) instead. secretSync: type: object additionalProperties: false @@ -1788,12 +1744,6 @@ properties: containerSecurityContext: *containerSecurityContext-spec image: *image-spec resources: *resources-spec - pdb: - type: object - additionalProperties: true - deprecated: true - description: | - DEPRECATED: Use [`proxy.chp.pdb`](schema_proxy.chp.pdb) instead. singleuser: type: object @@ -1992,13 +1942,6 @@ properties: type: boolean ip: type: string - enabled: - type: boolean - deprecated: true - description: | - Deprecated since chart version 0.11.0. Use - [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables) - instead. cmd: type: [array, string, "null"] @@ -2173,7 +2116,6 @@ properties: sudo rights for some of your users. This can be done by starting up as root, enabling it from the container in a startup script, and then transitioning to the normal user. - imagePullSecret: *deprecated-imagePullSecret scheduling: type: object @@ -2672,10 +2614,3 @@ properties: A flag that should only be set to true temporarily when experiencing a deprecation message that contain censored content that you wish to reveal. - - auth: - type: object - additionalProperties: true - deprecated: true - description: | - DEPRECATED: The Authenticator should be configured using [`hub.config`](schema_hub.config) instead. diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index 81b0c0800d..aa6060983b 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -101,53 +101,14 @@ If you have questions, please: {{- $breaking_title = print $breaking_title "\n#################################################################################" }} -{{- if hasKey .Values.hub "extraConfigMap" }} -{{- $breaking = print $breaking "\n\nRENAMED: hub.extraConfigMap has been renamed to custom" }} -{{- end }} - - -{{- if hasKey .Values "auth" }} -{{- if .Values.auth }} -{{- $breaking = print $breaking (include "jupyterhub.authDep.remapOldToNew" .) }} -{{- else }} -{{- $breaking = print $breaking "\n\nREMOVED: Please remove the empty 'auth' config" }} -{{- end }} -{{- end }} - - -{{- if hasKey .Values.proxy "containerSecurityContext" }} -{{- $breaking = print $breaking "\n\nRENAMED: proxy.containerSecurityContext has been renamed to proxy.chp.containerSecurityContext" }} -{{- end }} - - -{{- if hasKey .Values.proxy "pdb" }} -{{- $breaking = print $breaking "\n\nRENAMED: proxy.pdb has renamed to proxy.chp.pdb" }} -{{- end }} - - -{{- if hasKey .Values.proxy "networkPolicy" }} -{{- $breaking = print $breaking "\n\nRENAMED: proxy.networkPolicy has been renamed to proxy.chp.networkPolicy" }} -{{- end }} - - -{{- if hasKey .Values.hub "uid" }} -{{- $breaking = print $breaking "\n\nRENAMED: hub.uid must as of 1.0.0 be configured using hub.containerSecurityContext.runAsUser" }} -{{- end }} - - -{{- if hasKey .Values.hub "imagePullSecret" }} -{{- $breaking = print $breaking "\n\nREMOVED: hub.imagePullSecret has been removed, but there is now a chart wide wide configuration named imagePullSecret" }} -{{- end }} - - -{{- if hasKey .Values.singleuser "imagePullSecret" }} -{{- $breaking = print $breaking "\n\nREMOVED: singleuser.imagePullSecret has been removed, but there is now a chart wide wide configuration named imagePullSecret" }} -{{- end }} - +{{- /* + This is an example (in a helm template comment) on how to detect and + communicate with regards to a breaking chart config change. -{{- if hasKey .Values.singleuser.cloudMetadata "enabled" }} -{{- $breaking = print $breaking "\n\nCHANGED: singleuser.cloudMetadata.enabled must as of 1.0.0 be configured using singleuser.cloudMetadata.blockWithIptables with the opposite value." }} -{{- end }} + {{- if hasKey .Values.singleuser.cloudMetadata "enabled" }} + {{- $breaking = print $breaking "\n\nCHANGED: singleuser.cloudMetadata.enabled must as of 1.0.0 be configured using singleuser.cloudMetadata.blockWithIptables with the opposite value." }} + {{- end }} +*/}} {{- if $breaking }} diff --git a/jupyterhub/templates/_helpers-auth-rework.tpl b/jupyterhub/templates/_helpers-auth-rework.tpl deleted file mode 100644 index 2335398626..0000000000 --- a/jupyterhub/templates/_helpers-auth-rework.tpl +++ /dev/null @@ -1,223 +0,0 @@ -{{- /* - As we found Helm chart configuration of JupyterHub authentication was - too complicated both to maintain and use in its badly maintained state, - we sharply transitioned to a new system in 0.11.0. - - To help users transition, this file was developed. It converts the old - auth configuration under "auth" to the new under "c". We detect if the - old system is used and let the Helm chart's template rendering fail - where we also provide a useful error message including the new config - to use in its place. - - Implementation details: - - Secret content must be censored - - auth.state.cryptoKey needs a new home -*/}} - -{{- define "jupyterhub.authDep.classKeyToLong.map" }} -google: google -github: github -cilogon: cilogon -gitlab: gitlab -azuread: azuread -mediawiki: mediawiki -globus: globus -hmac: hmacauthenticator.HMACAuthenticator -dummy: dummy -tmp: tmpauthenticator.TmpAuthenticator -lti: ltiauthenticator.LTIAuthenticator -ldap: ldapauthenticator.LDAPAuthenticator -{{- end }} - -{{- /* Uses the map above, taking a keys, returning a value. */}} -{{- define "jupyterhub.authDep.classKeyToLong" -}} - {{- $map := include "jupyterhub.authDep.classKeyToLong.map" . | fromYaml }} - {{- index $map . }} -{{- end }} - -{{- define "jupyterhub.authDep.remapOldToNew.map" }} -scopes: OAuthenticator.scope -state.enabled: Authenticator.enable_auth_state -state.cryptoKey: CryptKeeper.keys -admin.access: JupyterHub.admin_access -admin.users: Authenticator.admin_users -whitelist.users: Authenticator.allowed_users -allowedUsers: Authenticator.allowed_users -google.clientId: GoogleOAuthenticator.client_id -google.clientSecret: GoogleOAuthenticator.client_secret -google.callbackUrl: GoogleOAuthenticator.oauth_callback_url -google.hostedDomain: GoogleOAuthenticator.hosted_domain -google.loginService: GoogleOAuthenticator.login_service -github.clientId: GitHubOAuthenticator.client_id -github.clientSecret: GitHubOAuthenticator.client_secret -github.callbackUrl: GitHubOAuthenticator.oauth_callback_url -github.orgWhitelist: GitHubOAuthenticator.allowed_organizations -github.allowedOrganizations: GitHubOAuthenticator.allowed_organizations -cilogon.clientId: CILogonOAuthenticator.client_id -cilogon.clientSecret: CILogonOAuthenticator.client_secret -cilogon.callbackUrl: CILogonOAuthenticator.oauth_callback_url -gitlab.clientId: GitLabOAuthenticator.client_id -gitlab.clientSecret: GitLabOAuthenticator.client_secret -gitlab.callbackUrl: GitLabOAuthenticator.oauth_callback_url -gitlab.gitlabGroupWhitelist: GitLabOAuthenticator.allowed_gitlab_groups -gitlab.allowedGitlabGroups: GitLabOAuthenticator.allowed_gitlab_groups -gitlab.gitlabProjectIdWhitelist: GitLabOAuthenticator.allowed_project_ids -gitlab.allowedProjectIds: GitLabOAuthenticator.allowed_project_ids -gitlab.gitlabUrl: GitLabOAuthenticator.gitlab_url -azuread.clientId: AzureAdOAuthenticator.client_id -azuread.clientSecret: AzureAdOAuthenticator.client_secret -azuread.callbackUrl: AzureAdOAuthenticator.oauth_callback_url -azuread.tenantId: AzureAdOAuthenticator.tenant_id -azuread.usernameClaim: AzureAdOAuthenticator.username_claim -mediawiki.clientId: MWOAuthenticator.client_id -mediawiki.clientSecret: MWOAuthenticator.client_secret -mediawiki.callbackUrl: MWOAuthenticator.oauth_callback_url -mediawiki.indexUrl: MWOAuthenticator.index_url -globus.clientId: GlobusOAuthenticator.client_id -globus.clientSecret: GlobusOAuthenticator.client_secret -globus.callbackUrl: GlobusOAuthenticator.oauth_callback_url -hmac.secretKey: HMACAuthenticator.secret_key -dummy.password: DummyAuthenticator.password -lti.consumers: LTIAuthenticator.consumers -ldap.server.address: LDAPAuthenticator.server_address -ldap.server.port: LDAPAuthenticator.server_port -ldap.server.ssl: LDAPAuthenticator.use_ssl -ldap.allowedGroups: LDAPAuthenticator.allowed_groups -ldap.dn.templates: LDAPAuthenticator.bind_dn_template -ldap.dn.lookup: LDAPAuthenticator.lookup_dn -ldap.dn.search.filter: LDAPAuthenticator.lookup_dn_search_filter -ldap.dn.search.user: LDAPAuthenticator.lookup_dn_search_user -ldap.dn.search.password: LDAPAuthenticator.lookup_dn_search_password -ldap.dn.user.dnAttribute: LDAPAuthenticator.lookup_dn_user_dn_attribute -ldap.dn.user.escape: LDAPAuthenticator.escape_userdn -ldap.dn.user.validRegex: LDAPAuthenticator.valid_username_regex -ldap.dn.user.searchBase: LDAPAuthenticator.user_search_base -ldap.dn.user.attribute: LDAPAuthenticator.user_attribute -ldap.dn.user.useLookupName: LDAPAuthenticator.use_lookup_dn_username -{{- end }} - -{{- /* Uses the map above, taking a keys, returning a value. */}} -{{- define "jupyterhub.authDep.remapOldToNew.single" }} - {{- $map := include "jupyterhub.authDep.remapOldToNew.map" . | fromYaml }} - {{- index $map . }} -{{- end }} - - -{{- /* - Recursively flattens a passed dict structure, outputs the result - in a passed dict which is assumed to be empty. -*/}} -{{- define "jupyterhub.flattenDict" }} - {{- $out := index . 0 }} - {{- $dict := index . 1 }} - - {{- $label := "" }} - {{- if eq (len .) 3 }} - {{- $label = index . 2 }} - {{- end }} - - {{- range $key, $val := $dict }} - {{- $sublabel := list $label $key | join "." | trimPrefix "." }} - {{- if kindOf $val | eq "map" }} - {{- include "jupyterhub.flattenDict" (list $out $val $sublabel) }} - {{- else }} - {{- $_ := set $out $sublabel $val }} - {{- end }} - {{- end }} -{{- end }} - -{{- /* - Remaps Recursively flattens a passed dict structure, outputs the result - in a passed dict which is assumed to be empty. -*/}} -{{- define "jupyterhub.authDep.remapOldToNew.mappable" -}} - {{- $c := index . 0 }} - {{- $safe := index . 1 }} - {{- range $key, $val := $c }} - {{- if not $safe }} - {{- $val = "***" }} - {{- end }} - {{- $_ := unset $c $key }} - {{- $class_dot_new_key := include "jupyterhub.authDep.remapOldToNew.single" $key }} - - {{- /* Manage unrecognized config */}} - {{- if eq $class_dot_new_key "" }} - {{- if not (hasKey $c "WarningUnrecognizedConfig") }} - {{- $_ := set $c "WarningUnrecognizedConfig" dict }} - {{- end }} - {{- $_ := set (index $c "WarningUnrecognizedConfig") $key $val }} - - {{- else }} - {{- /* Config value transformations if needed */}} - {{- if eq $key "state.cryptoKey" }} - {{- $val = list $val }} - {{- end }} - - {{- /* De-flatten the "ClassName.config_name" like strings */}} - {{- $class := splitList "." $class_dot_new_key | first }} - {{- $new_key := splitList "." $class_dot_new_key | last }} - {{- if not (hasKey $c $class) }} - {{- $_ := set $c $class dict }} - {{- end }} - {{- $_ := set (index $c $class) $new_key $val }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "jupyterhub.authDep.remapOldToNew" -}} - {{- $c := dict }} - {{- $result := (dict "hub" (dict "config" $c)) }} - {{- /* - Flattens the config in .Values.auth to a format of - "keyX.keyY...": "value". Writes output to $c. - */}} - {{- include "jupyterhub.flattenDict" (list $c (omit .Values.auth "type" "custom")) }} - - {{- /* - Transform the flattened config using a dictionary - representing the old z2jh config, output the result - in $c. - */}} - {{- include "jupyterhub.authDep.remapOldToNew.mappable" (list $c .Values.global.safeToShowValues) }} - - {{- $class_old_config_key := .Values.auth.type | default "" }} {{- /* ldap - github */}} - {{- $class_new_entrypoint := "" }} {{- /* ldapauthenticator.LDAPAuthenticator - github */}} - {{- $class_new_config_key := "" }} {{- /* LDAPAuthenticator - GitHubOAuthenticator */}} - - {{- /* SET $class_new_entrypoint, $class_new_config_key */}} - {{- if eq $class_old_config_key "custom" }} - {{- $class_new_entrypoint = .Values.auth.custom.className | default "custom.className wasn't configured!" }} - {{- $class_new_config_key = $class_new_entrypoint | splitList "." | last }} - {{- /* UPDATE c dict explicitly with auth.custom.config */}} - {{- if .Values.auth.custom.config }} - {{- $custom_config := merge (dict) .Values.auth.custom.config }} - {{- if not .Values.global.safeToShowValues }} - {{- range $key, $val := $custom_config }} - {{- $_ := set $custom_config $key "***" }} - {{- end }} - {{- end }} - {{- $_ := set $c $class_new_config_key $custom_config }} - {{- end }} - {{- else }} - {{- $class_new_entrypoint = include "jupyterhub.authDep.classKeyToLong" $class_old_config_key }} - {{- end }} - - {{- /* UPDATE c dict authenticator_class */}} - {{- if ne $class_new_entrypoint "" }} - {{- $_ := merge $c (dict "JupyterHub" (dict "authenticator_class" $class_new_entrypoint)) }} - {{- end }} - -{{- /* Output a sensible error message */}} - -The JupyterHub Helm chart's auth config has been reworked and requires changes. - -The new way to configure authentication in chart version 0.11.0+ is printed -below for your convenience. The values are not shown by default to ensure no -secrets are exposed, run helm upgrade with --set global.safeToShowValues=true -to show them. - -{{ $result | toYaml }} - -For more details, please see the updated auth config documentation at: -https://zero-to-jupyterhub.readthedocs.io/en/latest/administrator/authentication.html -{{- end }} From c5db2d359683b604c189e375f2b6700643f834a3 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 23 Sep 2021 16:53:48 +0200 Subject: [PATCH 021/898] example configurations for UI choices - consistent config, works for both 1.x and 2.x - examples: notebook, lab, lab on legacy server, nbclassic, and retrolab --- .../customizing/user-environment.md | 143 +++++++++++++++++- 1 file changed, 136 insertions(+), 7 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 90c8343ea5..52c6dd47a0 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -69,28 +69,157 @@ If you'd like users to select an environment from **multiple docker images**, see {ref}`multiple-profiles`. ``` +(user-interfaces)= + +## Selecting a user interface + +[JupyterLab][] is the new user interface for Jupyter, +which is meant to replace the classic notebook user interface (UI). +While users already can interchange `/tree` and `/lab` in the URL to switch between +the classic UI and JupyterLab if both are installed, +Deployments using JupyterHub 1.x and earlier default to the classic UI, +while 2.0 will make JupyterLab the default. + +[jupyterlab]: https://jupyterlab.readthedocs.io + +There are two things to customize, when picking the user interface to launch for users: + +1. the user interface (UI) +2. the server program to launch + +There are two main Jupyter server implementations. +_Most_ deployments will not see a difference, +but there can be issues for certain server extensions. + +1. the modern `jupyter server`, + which is launched when you use `jupyter lab` or other recent Jupyter applications, and +2. the 'classic' legacy notebook server (`jupyter notebook`) + +In general, the default UI is selected in {term}`config.yaml` by + +```yaml +singleuser: + defaultUrl: ... +``` + +and the default server by: + +```yaml +singleuser: + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: "..." +``` + +To select the modern server: + +```yaml +# this is the default with JupyterHub 2.0 +singleuser: + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" +``` + +or the classic notebook server: + +```yaml +# the default with JupyterHub 1.x +singleuser: + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" +``` + +You only need the above configuration when it is different from the default. +JupyterHub 2.0 changes the default server from `NotebookApp` to `ServerApp`, +so here we make the choice explicit in each example, +so the same configuration produces the same result with JupyterHub 1.x and 2.x. +That way, your choice will be preserved across upgrades. + (jupyterlab-by-default)= ## Use JupyterLab by default -[JupyterLab](https://jupyterlab.readthedocs.io/en/stable/index.html) is a new -user interface for Jupyter about to replace the classic user interface (UI). -While users already can interchange `/tree` and `/lab` in the URL to switch between -the classic UI and JupyterLab, they will default to use the classic UI. +:::{note} +This will be the default in JupyterHub 2.0 and helm chart 2.0. +::: -The classic Jupyter Notebook UI is the default provided, but you can change it to -JupyterLab with the following config in your {term}`config.yaml`: +You can choose JupyterLab as the default UI with the following config in your {term}`config.yaml`: ```yaml singleuser: defaultUrl: "/lab" + extraEnv: + JUPYERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" ``` -```{note} +You can also make JupyterLab the default UI _without_ upgrading to the newer server implementation. +This may help users who need to stick to the legacy UI with extensions that may not work on the new server. + +```yaml +singleuser: + defaultUrl: "/lab" + extraEnv: + JUPYERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" +``` + +````{note} You need the `jupyterlab` package (installable via `pip` or `conda`) for this to work. All images in the [jupyter/docker-stacks repository](https://github.com/jupyter/docker-stacks/) come pre-installed with it. ``` +(classic-by-default)= + +### Use classic notebook by default + +:::{note} +This is the default in JupyterHub 1.x and helm chart 1.x. +::: + +If you aren't ready to upgrade to JupyterLab, +especially for those who depend on custom notebook extensions without an equivalent in JupyterLab, +you can always stick with the legacy notebook server (`jupyter notebook`): + +```yaml +# the default with JupyterHub 1.x +singleuser: + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" +```` + +This will start the exact same server and UI as before. + +If you install the `nbclassic` package, +you can also default to the classic UI, running on the new server: +This may be the best way to support users on both classic and new environments. + +```yaml +singleuser: + defaultUrl: /tree/ + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" +``` + +### Alternative interfaces + +There are more Jupyter server extensions providing alternate UI choices, +which can be used with JupyterHub. + +For example, [retrolab][] is a different, built on JupyterLab, +but which may be more comfortable for those coming from the classic Jupyter UI. + +[retrolab]: https://blog.jupyter.org/retrolab-a-jupyterlab-distribution-with-a-retro-look-and-feel-8096b8b223d0 + +To install such an extension: + +1. install the package (`pip install retrolab` or `conda install retrolab`) in your user image +2. configure the default URL, and make sure ServerApp is used: + +```yaml +singleuser: + defaultUrl: /retro/ + extraEnv: + JUPYTERHUB_SINGLEUSER_APP: jupyter_server.serverapp.ServerApp +``` + (custom-docker-image)= ## Customize an existing Docker image From 57b4a08e19db896b83b4f225911853a316e83eed Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 24 Sep 2021 09:20:32 +0200 Subject: [PATCH 022/898] Apply suggestions from code review Co-authored-by: Simon Li --- .../jupyterhub/customizing/user-environment.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 52c6dd47a0..634660e3bd 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -75,10 +75,10 @@ see {ref}`multiple-profiles`. [JupyterLab][] is the new user interface for Jupyter, which is meant to replace the classic notebook user interface (UI). -While users already can interchange `/tree` and `/lab` in the URL to switch between -the classic UI and JupyterLab if both are installed, +Users can already interchange `/tree` and `/lab` in the URL to switch between +the classic UI and JupyterLab if both are installed. Deployments using JupyterHub 1.x and earlier default to the classic UI, -while 2.0 will make JupyterLab the default. +while JupyterHub 2.0 makes JupyterLab the default. [jupyterlab]: https://jupyterlab.readthedocs.io @@ -139,7 +139,7 @@ That way, your choice will be preserved across upgrades. ## Use JupyterLab by default :::{note} -This will be the default in JupyterHub 2.0 and helm chart 2.0. +This is the default in JupyterHub 2.0 and Helm chart 2.0. ::: You can choose JupyterLab as the default UI with the following config in your {term}`config.yaml`: @@ -203,14 +203,14 @@ singleuser: There are more Jupyter server extensions providing alternate UI choices, which can be used with JupyterHub. -For example, [retrolab][] is a different, built on JupyterLab, +For example, [retrolab][] is a different notebook interface, built on JupyterLab, but which may be more comfortable for those coming from the classic Jupyter UI. [retrolab]: https://blog.jupyter.org/retrolab-a-jupyterlab-distribution-with-a-retro-look-and-feel-8096b8b223d0 To install such an extension: -1. install the package (`pip install retrolab` or `conda install retrolab`) in your user image +1. install the package (`pip install retrolab` or `conda install retrolab`) in your user container image 2. configure the default URL, and make sure ServerApp is used: ```yaml From 16595c74fb5eceaeda8d23d326d225b209fe61bf Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sat, 25 Sep 2021 19:31:40 +0100 Subject: [PATCH 023/898] ci: use PVCs when testing upgrades This ensures we're testing whether persistent data (like the database) is actually upgraded --- .github/workflows/test-chart.yaml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 3792224e4b..4339c0146d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -218,6 +218,15 @@ jobs: timeout: 150 max-restarts: 1 + - name: Set matrix dependent Helm arguments + run: | + if [ "${{ matrix.test }}" = "upgrade" ]; then + HELM_EXTRA_ARGS="--set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic" + else + HELM_EXTRA_ARGS="" + fi + echo HELM_EXTRA_ARGS="$HELM_EXTRA_ARGS" >> $GITHUB_ENV + - name: "(Upgrade) Install ${{ matrix.upgrade-from }} chart" if: matrix.test == 'upgrade' run: | @@ -233,7 +242,7 @@ jobs: # # https://github.com/helm/helm/issues/9244 cd ci - helm install jupyterhub --repo https://jupyterhub.github.io/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} + helm install jupyterhub --repo https://jupyterhub.github.io/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} $HELM_EXTRA_ARGS - name: "(Upgrade) Install helm diff" if: matrix.test == 'upgrade' @@ -266,6 +275,7 @@ jobs: echo helm diff upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml \ + $HELM_EXTRA_ARGS \ --show-secrets \ --context=3 \ --post-renderer=ci/string-replacer.sh @@ -291,7 +301,7 @@ jobs: - name: "Install local chart" run: | - helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml ${{ matrix.local-chart-extra-args }} + helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml ${{ matrix.local-chart-extra-args }} $HELM_EXTRA_ARGS - name: "Await local chart" uses: jupyterhub/action-k8s-await-workloads@v1 From e290867c83df378f64d6b429fd7ef624b1d41df4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sat, 25 Sep 2021 20:41:01 +0100 Subject: [PATCH 024/898] Attempt to use upgrade-from-extra-args local-chart-extra-args --- .github/workflows/test-chart.yaml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 4339c0146d..4a37d1c471 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -154,12 +154,22 @@ jobs: --set proxy.secretToken=aaaa1111 --set hub.cookieSecret=bbbb2222 --set hub.config.CryptKeeper.keys[0]=cccc3333 + --set hub.db.type=sqlite-pvc + --set singleuser.storage.type=dynamic + local-chart-extra-args: >- + --set hub.db.type=sqlite-pvc + --set singleuser.storage.type=dynamic create-k8s-test-resources: true - k3s-channel: v1.19 test: upgrade upgrade-from: dev upgrade-from-extra-args: >- --set proxy.secretToken=aaaa1111 + --set hub.db.type=sqlite-pvc + --set singleuser.storage.type=dynamic + local-chart-extra-args: >- + --set hub.db.type=sqlite-pvc + --set singleuser.storage.type=dynamic steps: - uses: actions/checkout@v2 @@ -218,15 +228,6 @@ jobs: timeout: 150 max-restarts: 1 - - name: Set matrix dependent Helm arguments - run: | - if [ "${{ matrix.test }}" = "upgrade" ]; then - HELM_EXTRA_ARGS="--set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic" - else - HELM_EXTRA_ARGS="" - fi - echo HELM_EXTRA_ARGS="$HELM_EXTRA_ARGS" >> $GITHUB_ENV - - name: "(Upgrade) Install ${{ matrix.upgrade-from }} chart" if: matrix.test == 'upgrade' run: | @@ -242,7 +243,7 @@ jobs: # # https://github.com/helm/helm/issues/9244 cd ci - helm install jupyterhub --repo https://jupyterhub.github.io/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} $HELM_EXTRA_ARGS + helm install jupyterhub --repo https://jupyterhub.github.io/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} - name: "(Upgrade) Install helm diff" if: matrix.test == 'upgrade' @@ -275,7 +276,7 @@ jobs: echo helm diff upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml \ - $HELM_EXTRA_ARGS \ + ${{ matrix.local-chart-extra-args }} \ --show-secrets \ --context=3 \ --post-renderer=ci/string-replacer.sh @@ -301,7 +302,7 @@ jobs: - name: "Install local chart" run: | - helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml ${{ matrix.local-chart-extra-args }} $HELM_EXTRA_ARGS + helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml ${{ matrix.local-chart-extra-args }} - name: "Await local chart" uses: jupyterhub/action-k8s-await-workloads@v1 From da056a65c8b65a8fee143b4a3baa3ac42666b0c9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 00:42:37 +0200 Subject: [PATCH 025/898] ci: test against k8s 1.22 --- .github/workflows/test-chart.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 4a37d1c471..fb37068d81 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -115,11 +115,13 @@ jobs: # k3s-version: https://github.com/rancher/k3s/tags # k3s-channel: https://update.k3s.io/v1-release/channels include: - - k3s-channel: v1.21 + - k3s-channel: v1.22 test: install debuggable: debuggable - k3s-channel: v1.20 test: install + - k3s-channel: v1.20 + test: install - k3s-channel: v1.19 test: install - k3s-channel: v1.18 # also test prePuller.hook From 7ce8dd69a45e37599dbb695ccce61b0085fb5b40 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 15:19:56 +0200 Subject: [PATCH 026/898] Use f-strings instead of the % pattern --- doc/source/conf.py | 6 +++--- jupyterhub/files/hub/jupyterhub_config.py | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 747748fba0..df6109499c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -222,15 +222,15 @@ def parse_schema(d, md=[], depth=0, pre=""): depth += 1 # Create markdown headers for each schema level for key, val in d["properties"].items(): - md.append("(schema_%s)=" % (pre + key)) - md.append("#" * (depth + 1) + " " + pre + key) + md.append(f"(schema_{pre}{key})=") + md.append("#" * (depth + 1) + f" {pre}{key}") md.append("") if "description" in val: for ln in val["description"].split("\n"): md.append(ln) md.append("") - parse_schema(val, md, depth, pre + f"{key}.") + parse_schema(val, md, depth, f"{pre}{key}.") depth -= 1 return md diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index a9e41a0e8b..7e9e368ee8 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -224,7 +224,7 @@ def camelCaseify(s): pass else: raise ValueError( - "Unrecognized value for matchNodePurpose: %r" % match_node_purpose + f"Unrecognized value for matchNodePurpose: {match_node_purpose}" ) # Combine the common tolerations for user pods with singleuser tolerations @@ -347,15 +347,15 @@ def camelCaseify(s): cull_timeout = get_config("cull.timeout") if cull_timeout: - cull_cmd.append("--timeout=%s" % cull_timeout) + cull_cmd.append(f"--timeout={cull_timeout}") cull_every = get_config("cull.every") if cull_every: - cull_cmd.append("--cull-every=%s" % cull_every) + cull_cmd.append(f"--cull-every={cull_every}") cull_concurrency = get_config("cull.concurrency") if cull_concurrency: - cull_cmd.append("--concurrency=%s" % cull_concurrency) + cull_cmd.append(f"--concurrency={cull_concurrency}") if get_config("cull.users"): cull_cmd.append("--cull-users") @@ -366,7 +366,7 @@ def camelCaseify(s): cull_max_age = get_config("cull.maxAge") if cull_max_age: - cull_cmd.append("--max-age=%s" % cull_max_age) + cull_cmd.append(f"--max-age={cull_max_age}") c.JupyterHub.services.append( { @@ -459,5 +459,5 @@ def camelCaseify(s): # execute hub.extraConfig entries for key, config_py in sorted(get_config("hub.extraConfig", {}).items()): - print("Loading extra config: %s" % key) + print(f"Loading extra config: {key}") exec(config_py) From 1770344be5e0beb538d2af61e08c23ca326cd589 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 15:26:41 +0200 Subject: [PATCH 027/898] Update links that redirected --- .github/dependabot.yaml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 81dbf0631a..08f41576e5 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,4 +1,4 @@ -# dependabot.yml reference: https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates +# dependabot.yml reference: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates # # Notes: # - Status and logs from dependabot are provided at diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fe7002ec13..cc1556cdc6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # name: Publish diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 4a37d1c471..9a7ecfc4a2 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # name: Test chart diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 4e4ba16c2e..89796a69a3 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # name: Test docker multiarch build diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 91f0033150..477fad2e50 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # name: Test docs diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 360645d5cf..8f25f20d89 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # # This workflow use aquasecurity/trivy to scan the images we have published for # known vulnerabilities. If there are such that can be patched, we let this From c3470ed468dfebba93b3e9094aded9ca10a5a268 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 15:41:18 +0200 Subject: [PATCH 028/898] Retrospectively add breaking change to changelog entry 0.10.0 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e48466997..e9dc0f3dcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -784,6 +784,17 @@ the Helm chart to easier comply with PodSecurityPolicies by default. #### Breaking changes: +- KubeSpawner was updated to include a breaking change influencing users of + named servers. + + > Security fix: CVE-2020-15110 / GHSA-v7m9-9497-p9gr. When named-servers are + > enabled, certain username patterns, depending on authenticator, could allow + > collisions. The default named-server template is changed to prevent + > collisions, meaning that upgrading will lose associations of named-servers + > with their PVCs if the default templates are used. Data should not be lost + > (old PVCs will be ignored, not deleted), but will need manual migration to + > new PVCs prior to deletion of old PVCs. + - Anyone relying on configuration in the `proxy.https` section are now explicitly required to set `proxy.https.enabled` to `true`. From 3f45e259ef2256765b1c133f3f2615026bfd6de6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 25 Sep 2021 21:38:08 +0200 Subject: [PATCH 029/898] Update NOTES.txt --- jupyterhub/templates/NOTES.txt | 114 ++++++++++++++++++++---------- jupyterhub/templates/_helpers.tpl | 18 +++++ 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index aa6060983b..36f98b39f8 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -1,54 +1,90 @@ -Thank you for installing JupyterHub! +{{- $proxy_service := include "jupyterhub.proxy-public.fullname" . -}} -Your release is named "{{ .Release.Name }}" and installed into the namespace "{{ .Release.Namespace }}". +. __ __ __ __ __ + / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ + __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ +/ /_/ / / /_/ / / /_/ / / /_/ / / /_ / __/ / / / __ / / /_/ / / /_/ / +\____/ \__,_/ / .___/ \__, / \__/ \___/ /_/ /_/ /_/ \__,_/ /_.___/ + /_/ /____/ -You can check whether the hub and proxy are ready by running: + You have successfully installed the official JupyterHub Helm chart! - kubectl --namespace={{ .Release.Namespace }} get pod +### Installation info -and watching for both those pods to be in status 'Running'.{{ println }} + - Kubernetes namespace: {{ .Release.Namespace }} + - Helm release name: {{ .Release.Name }} + - Helm chart version: {{ .Chart.Version }} + - JupyterHub version: {{ .Chart.AppVersion }} + - Hub pod software: See https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{{ include "jupyterhub.chart-version-to-git-ref" .Chart.Version }}/images/hub/requirements.txt -{{- if eq .Values.proxy.service.type "LoadBalancer" }} -You can find the public (load-balancer) IP of JupyterHub by running: +### Followup links - kubectl -n {{ .Release.Namespace }} get svc {{ include "jupyterhub.proxy-public.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[].ip}' + - Documentation: https://z2jh.jupyter.org + - Help forum: https://discourse.jupyter.org + - Social chat: https://gitter.im/jupyterhub/jupyterhub + - Issue tracking: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues -It might take a few minutes for it to appear! -{{- end }} -{{- if eq .Values.proxy.service.type "ClusterIP" }} -You can find the internal (cluster) IP of JupyterHub by running: +### Post-installation checklist - kubectl get -n {{ .Release.Namespace }} svc {{ include "jupyterhub.proxy-public.fullname" . }} -o jsonpath='{.spec.clusterIP}' -{{- end }} -{{- if eq .Values.proxy.service.type "NodePort" }} -You can find the NodePorts of JupyterHub by running: + - Verify that created Pods enter a Running state: - kubectl --namespace={{ .Release.Namespace }} get svc {{ include "jupyterhub.proxy-public.fullname" . }} -o jsonpath='{range .spec.ports[*]} {.name}: {.port}{"\n"} {end}' -{{- end }} + kubectl --namespace={{ .Release.Namespace }} get pod -{{- if .Values.ingress.enabled }} + If a pod is stuck with a Pending or ContainerCreating status, diagnose with: -You should be able to access JupyterHub using your configured ingress at: -{{ range $host := .Values.ingress.hosts }} - http://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ -{{- end }} -{{- range $tls := .Values.ingress.tls }} - {{- range $host := $tls.hosts }} - https://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ - {{- end }} -{{- end }} + kubectl --namespace={{ .Release.Namespace }} describe pod -{{- end }} + If a pod keeps restarting, diagnose with: + + kubectl --namespace={{ .Release.Namespace }} logs --previous + {{- println }} -To get full information about the JupyterHub proxy service run: + {{- if eq .Values.proxy.service.type "LoadBalancer" }} + - Verify an external IP is provided for the k8s Service {{ $proxy_service }}. - kubectl --namespace={{ .Release.Namespace }} get svc {{ include "jupyterhub.proxy-public.fullname" . }} + kubectl --namespace={{ .Release.Namespace }} get service {{ $proxy_service }} -If you have questions, please: + If the external ip remains , diagnose with: - 1. Read the guide at https://z2jh.jupyter.org - 2. Ask for help or chat to us on https://discourse.jupyter.org/ - 3. If you find a bug please report it at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues + kubectl --namespace={{ .Release.Namespace }} describe service {{ $proxy_service }} + {{- end }} + + - Verify web based access: + {{- println }} + {{- if .Values.ingress.enabled }} + {{- range $host := .Values.ingress.hosts }} + Try insecure HTTP access: http://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ + {{- end }} + + {{- range $tls := .Values.ingress.tls }} + {{- range $host := $tls.hosts }} + Try secure HTTPS access: https://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ + {{- end }} + {{- end }} + {{- else }} + You have not configured a k8s Ingress resource so you need to access the k8s + Service {{ $proxy_service }} directly. + {{- println }} + + {{- if eq .Values.proxy.service.type "NodePort" }} + The k8s Service {{ $proxy_service }} is exposed via NodePorts. That means + that all the k8s cluster's nodes are exposing the k8s Service via those + ports. + + Try insecure HTTP access: http://:{{ .Values.proxy.service.nodePorts.http | default "no-http-nodeport-set"}} + Try secure HTTPS access: https://:{{ .Values.proxy.service.nodePorts.https | default "no-https-nodeport-set" }} + + {{- else }} + If your computer is outside the k8s cluster, you can port-forward traffic to + the k8s Service {{ $proxy_service }} with kubectl to access it from your + computer. + + kubectl --namespace={{ .Release.Namespace }} port-forward service/{{ $proxy_service }} 8080:http + + Try insecure HTTP access: http://localhost:8080 + {{- end }} + {{- end }} + {{- println }} @@ -58,7 +94,7 @@ If you have questions, please: Warnings for likely misconfiguration */}} -{{- if and (not .Values.scheduling.podPriority.enabled) (and .Values.scheduling.userPlaceholder.enabled .Values.scheduling.userPlaceholder.replicas) }}{{ println }} +{{- if and (not .Values.scheduling.podPriority.enabled) (and .Values.scheduling.userPlaceholder.enabled .Values.scheduling.userPlaceholder.replicas) }} ################################################################################# ###### WARNING: You are using user placeholders without pod priority ##### ###### enabled*, either enable pod priority or stop using the ##### @@ -69,16 +105,18 @@ If you have questions, please: ###### **scheduling.userPlaceholder.enabled ##### ###### **scheduling.userPlaceholder.replicas ##### ################################################################################# +{{- println }} {{- end }} {{- if eq .Values.proxy.https.enabled false }} -{{- if or (not (eq .Values.proxy.https.type "letsencrypt")) (not (eq (.Values.proxy.https.letsencrypt.contactEmail | default "") "")) }}{{ println }} +{{- if or (not (eq .Values.proxy.https.type "letsencrypt")) (not (eq (.Values.proxy.https.letsencrypt.contactEmail | default "") "")) }} ################################################################################# ###### WARNING: proxy.https.enabled is set to false by default since ##### ###### version 0.10.0. It is now set to false but proxy.https ##### ###### has been modified indicating you may want it enabled. ##### ################################################################################# +{{- println }} {{- end }} {{- end }} @@ -112,5 +150,5 @@ If you have questions, please: {{- if $breaking }} -{{- fail (print $breaking_title $breaking) }} +{{- fail (print $breaking_title $breaking "\n\n") }} {{- end }} diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl index 53bced8f71..10b31c7058 100644 --- a/jupyterhub/templates/_helpers.tpl +++ b/jupyterhub/templates/_helpers.tpl @@ -361,3 +361,21 @@ limits: {{- print "\n\nextraFiles entries (" $file_key ") must only contain one of the fields: 'data', 'stringData', and 'binaryData'." | fail }} {{- end }} {{- end }} + +{{- /* + jupyterhub.chart-version-to-git-ref: + Renders a valid git reference from a chartpress generated version string. + In practice, either a git tag or a git commit hash will be returned. + + - The version string will follow a chartpress pattern, see + https://github.com/jupyterhub/chartpress#examples-chart-versions-and-image-tags. + + - The regexReplaceAll function is a sprig library function, see + http://masterminds.github.io/sprig/strings.html. + + - The regular expression is in golang syntax, but \d had to become \\d for + example. +*/}} +{{- define "jupyterhub.chart-version-to-git-ref" -}} +{{- regexReplaceAll ".*[.-]n\\d+[.]h(.*)" . "${1}" }} +{{- end }} From 0476847ce085dc83bdb8833348d61a6ce118708d Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 27 Sep 2021 09:03:49 +0200 Subject: [PATCH 030/898] Apply suggestions from code review Co-authored-by: Carol Willing --- .../jupyterhub/customizing/user-environment.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 634660e3bd..e23abec75a 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -82,14 +82,14 @@ while JupyterHub 2.0 makes JupyterLab the default. [jupyterlab]: https://jupyterlab.readthedocs.io -There are two things to customize, when picking the user interface to launch for users: +To pick a user interface to launch by default for users, two customization items need to be set: -1. the user interface (UI) +1. the preferred default user interface (UI) 2. the server program to launch -There are two main Jupyter server implementations. -_Most_ deployments will not see a difference, -but there can be issues for certain server extensions. +There are two main Jupyter server implementations +(_Most deployments will not see a difference, +but there can be issues for certain server extensions. If unsure, new applications should choose `jupyter_server`._): 1. the modern `jupyter server`, which is launched when you use `jupyter lab` or other recent Jupyter applications, and @@ -110,7 +110,7 @@ singleuser: JUPYTERHUB_SINGLEUSER_APP: "..." ``` -To select the modern server: +Specifically, use one of these options to select the modern server: ```yaml # this is the default with JupyterHub 2.0 From c0b4738882f76da3101689a9756031f9d5412244 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 19:43:13 +0000 Subject: [PATCH 031/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.26.0 → v2.28.0](https://github.com/asottile/pyupgrade/compare/v2.26.0...v2.28.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9ce7e92a25..71bfcb8182 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.26.0 + rev: v2.28.0 hooks: - id: pyupgrade args: From 24433cd7d60b22c80aeb990b473297a21a21414e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:28:30 +0000 Subject: [PATCH 032/898] build(deps): bump py-spy from 0.3.9 to 0.3.10 in /images/hub Bumps [py-spy](https://github.com/benfred/py-spy) from 0.3.9 to 0.3.10. - [Release notes](https://github.com/benfred/py-spy/releases) - [Changelog](https://github.com/benfred/py-spy/blob/master/CHANGELOG.md) - [Commits](https://github.com/benfred/py-spy/compare/v0.3.9...v0.3.10) --- updated-dependencies: - dependency-name: py-spy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 46 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 4ae61a3049..8879714084 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -44,8 +44,6 @@ greenlet==1.1.1 # via sqlalchemy idna==3.2 # via requests -importlib-resources==5.2.2 - # via alembic jinja2==3.0.1 # via # jupyterhub @@ -54,6 +52,16 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub +jupyterhub==2.0.0b1 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator jupyterhub-firstuseauthenticator==0.14.1 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -70,16 +78,6 @@ jupyterhub-nativeauthenticator==0.0.7 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jupyterhub==2.0.0b1 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator kubernetes==18.20.0 # via jupyterhub-kubespawner ldap3==2.9.1 @@ -112,15 +110,15 @@ prometheus-client==0.11.0 # via jupyterhub psycopg2-binary==2.9.1 # via -r requirements.in -py-spy==0.3.9 +py-spy==0.3.10 # via -r requirements.in -pyasn1-modules==0.2.8 - # via google-auth pyasn1==0.4.8 # via # ldap3 # pyasn1-modules # rsa +pyasn1-modules==0.2.8 + # via google-auth pycparser==2.20 # via cffi pycurl==7.44.1 @@ -150,22 +148,22 @@ pyyaml==5.4.1 # via # jupyterhub-kubespawner # kubernetes -requests-oauthlib==1.3.0 - # via - # kubernetes - # mwoauth requests==2.26.0 # via # jupyterhub # kubernetes # mwoauth # requests-oauthlib +requests-oauthlib==1.3.0 + # via + # kubernetes + # mwoauth rsa==4.7.2 # via google-auth -ruamel.yaml.clib==0.2.6 - # via ruamel.yaml ruamel.yaml==0.17.16 # via jupyter-telemetry +ruamel.yaml.clib==0.2.6 + # via ruamel.yaml six==1.16.0 # via # bcrypt @@ -175,13 +173,13 @@ six==1.16.0 # onetimepass # pyopenssl # python-dateutil -sqlalchemy-cockroachdb==1.4.0 - # via -r requirements.in sqlalchemy==1.4.23 # via # alembic # jupyterhub # sqlalchemy-cockroachdb +sqlalchemy-cockroachdb==1.4.0 + # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -203,8 +201,6 @@ urllib3==1.26.6 # requests websocket-client==1.2.1 # via kubernetes -zipp==3.5.0 - # via importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools From d8aaaac0b29a2dc474906be0bfe601460d8834f2 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 28 Sep 2021 01:04:17 +0000 Subject: [PATCH 033/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 8efcf3b1b5..f5f16fd825 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-09-17_01:09:21 +# VULN_SCAN_TIME=2021-09-28_01:04:16 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 667faf974f7028dd7cffb1ed7cd40038643cead2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Sep 2021 21:26:32 +0200 Subject: [PATCH 034/898] ci: fix typo causing 2x k8s 1.20 tests --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 59cf7209e4..7c57d6301f 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -118,7 +118,7 @@ jobs: - k3s-channel: v1.22 test: install debuggable: debuggable - - k3s-channel: v1.20 + - k3s-channel: v1.21 test: install - k3s-channel: v1.20 test: install From b92d86cd150beddefe349c2c306c42e617192710 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 00:31:06 +0200 Subject: [PATCH 035/898] Add ingress.ingressClassName config option --- .github/workflows/test-chart.yaml | 6 +++++- jupyterhub/schema.yaml | 9 +++++++++ jupyterhub/templates/ingress.yaml | 3 +++ jupyterhub/values.yaml | 1 + tools/templates/lint-and-validate-values.yaml | 1 + 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 7c57d6301f..02bb124db0 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -136,6 +136,10 @@ jobs: --set proxy.secretToken=aaaa1111 --set hub.cookieSecret=bbbb2222 --set hub.config.CryptKeeper.keys[0]=cccc3333 + # ingress.ingressClassName requires k8s 1.18 and above, don't + # validate setting it against the k8s api-server on k8s 1.17. + helm-template-validate-extra-args: >- + --set ingress.ingressClassName="" create-k8s-test-resources: true # We run two upgrade tests where we first install an already released @@ -219,7 +223,7 @@ jobs: # dedicated lint-and-validate-values.yaml config. - name: "Helm template --validate (with lint and validate config)" run: | - helm template --validate jupyterhub ./jupyterhub --values tools/templates/lint-and-validate-values.yaml + helm template --validate jupyterhub ./jupyterhub --values tools/templates/lint-and-validate-values.yaml ${{ matrix.helm-template-validate-extra-args }} # It is only needed at this point forward as this is when we install # jupyterhub and the autohttps pod is about to start, so for CI diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 724e7ec89c..a022278d30 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2379,6 +2379,15 @@ properties: See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) for more details about annotations. + ingressClassName: + type: [string, "null"] + description: | + Maps directly to the Ingress resource's spec.ingressClassName. To + configure this, your k8s cluster must have version 1.18+ or above. + + See [the Kubernetes + documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) + for more details. hosts: type: array description: | diff --git a/jupyterhub/templates/ingress.yaml b/jupyterhub/templates/ingress.yaml index cc4f9ee119..a86753f85b 100644 --- a/jupyterhub/templates/ingress.yaml +++ b/jupyterhub/templates/ingress.yaml @@ -14,6 +14,9 @@ metadata: {{- . | toYaml | nindent 4 }} {{- end }} spec: + {{- with .Values.ingress.ingressClassName }} + ingressClassName: "{{ . }}" + {{- end }} rules: {{- range $host := .Values.ingress.hosts | default (list "") }} - http: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 52fdb33471..d6a3a068cb 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -539,6 +539,7 @@ prePuller: ingress: enabled: false annotations: {} + ingressClassName: hosts: [] pathSuffix: pathType: Prefix diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 657bcb24de..428b453939 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -497,6 +497,7 @@ prePuller: ingress: enabled: true annotations: *annotations + ingressClassName: mock-ingress-class-name hosts: - mocked1.domain.name - mocked2.domain.name From 9d481b9e62ef3334c81624dcaa38efaf2eec833d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Sep 2021 01:40:58 +0200 Subject: [PATCH 036/898] Add hub.loadRoles configuration The motivation for adding a dedicated chart configuration is that hub.config.JupyterHub.load_roles would end up being overridden easily if configured from multiple config files. So, having a dictionary configuration for the same thing can help. --- .github/workflows/test-chart.yaml | 4 ++- dev-config-local-chart-extra-config.yaml | 26 ++++++++++++++ jupyterhub/files/hub/jupyterhub_config.py | 7 ++++ jupyterhub/schema.yaml | 34 +++++++++++++++++++ jupyterhub/values.yaml | 1 + tools/templates/lint-and-validate-values.yaml | 8 +++++ 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 dev-config-local-chart-extra-config.yaml diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 02bb124db0..b83653a3d5 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -153,6 +153,7 @@ jobs: # # The upgrade-from input should match the version information from # https://jupyterhub.github.io/helm-chart/info.json + # - k3s-channel: v1.19 test: upgrade upgrade-from: stable @@ -282,6 +283,7 @@ jobs: echo helm diff upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml \ + --values dev-config-local-chart-extra-config.yaml \ ${{ matrix.local-chart-extra-args }} \ --show-secrets \ --context=3 \ @@ -308,7 +310,7 @@ jobs: - name: "Install local chart" run: | - helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml ${{ matrix.local-chart-extra-args }} + helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml --values dev-config-local-chart-extra-config.yaml ${{ matrix.local-chart-extra-args }} - name: "Await local chart" uses: jupyterhub/action-k8s-await-workloads@v1 diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml new file mode 100644 index 0000000000..ee8fe0df81 --- /dev/null +++ b/dev-config-local-chart-extra-config.yaml @@ -0,0 +1,26 @@ +# This config file is useful in the upgrade tests, where we upgrade from +# either the latest stable chart or the latest dev release of the chart. This +# config is only applied to the chart we upgrade to. It helps us handle +# situations when we add new configuration options that would fail with a schema +# validation error in the previous chart versions. +# +# Note that one could think that it would be possible to have dev-config.yaml +# include this config and then pass --set hub.some-option=null to null it out +# when it must not be passed, but that still triggers schema validation errors. +# +hub: + # FIXME: move loadRoles to dev-config.yaml after 2.0.0 is released. + loadRoles: + test-role-1: + description: Access to users' information and group membership + scopes: [users, groups] + users: [cyclops, gandalf] + services: [test] + groups: [] + test-role-2-explicit-name: + name: test-role-2 + description: Access to users' information and group membership + scopes: [users, groups] + users: [cyclops, gandalf] + services: [test] + groups: [] diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 7e9e368ee8..d53aab135d 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -388,6 +388,13 @@ def camelCaseify(s): c.JupyterHub.services.append(service) +for key, role in get_config("hub.loadRoles", {}).items(): + # c.JupyterHub.load_roles is a list of dicts, but + # hub.loadRoles is a dict of dicts to make the config mergable + role.setdefault("name", key) + + c.JupyterHub.load_roles.append(role) + set_config_if_not_none(c.Spawner, "cmd", "singleuser.cmd") set_config_if_not_none(c.Spawner, "default_url", "singleuser.defaultUrl") diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index a022278d30..d0a56295d1 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1201,6 +1201,40 @@ properties: An alias for api_token provided for backward compatibility by the JupyterHub Helm chart that will be transformed to api_token. + loadRoles: + type: object + additionalProperties: true + description: | + This is where you should define JupyterHub roles and apply them to + JupyterHub users, groups, and services to grant them additional + permissions as defined in JupyterHub's RBAC system. + + Complement this documentation with [JupyterHub's + documentation](https://jupyterhub.readthedocs.io/en/latest/rbac/roles.html#defining-roles) + about `load_roles`. + + Note that while JupyterHub's native configuration `load_roles` accepts + a list of role objects, this Helm chart only accept a dictionary where + each key represents the name of a role and the value is the actual + role object. + + ```yaml + hub: + loadRoles: + teacher: + description: Access to users' information and group membership + + # this role provides permissions to... + scopes: [users, groups] + + # this role will be assigned to... + users: [erik] + services: [grading-service] + groups: [teachers] + ``` + + When configuring JupyterHub roles via this Helm chart, the `name` + field can be omitted as it can be implied by the dictionary key. shutdownOnLogout: type: [boolean, "null"] description: *jupyterhub-native-config-description diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index d6a3a068cb..6453f6bbe4 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -88,6 +88,7 @@ hub: runAsGroup: 1000 allowPrivilegeEscalation: false lifecycle: {} + loadRoles: {} services: {} pdb: enabled: false diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 428b453939..3183354518 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -160,6 +160,14 @@ hub: oauth_roles: [dummy] info: key1: value1 + loadRoles: + test-role-1: + name: test-role-1 + description: Access to users' information and group membership + scopes: [users, groups] + users: [cyclops, gandalf] + services: [test-service-1] + groups: [test-group-1] pdb: enabled: true maxUnavailable: 1 From 33f42ac1043148238d2805a38bddd2e6e3f477db Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 00:24:06 +0200 Subject: [PATCH 037/898] Apply suggestions from code review Co-authored-by: Yuvi Panda --- jupyterhub/templates/NOTES.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index 36f98b39f8..abb0836711 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -1,5 +1,6 @@ {{- $proxy_service := include "jupyterhub.proxy-public.fullname" . -}} +{{- /* Generated with https://patorjk.com/software/taag/#p=display&h=0&f=Slant&t=JupyterHub */}} . __ __ __ __ __ / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ @@ -15,7 +16,7 @@ - Helm release name: {{ .Release.Name }} - Helm chart version: {{ .Chart.Version }} - JupyterHub version: {{ .Chart.AppVersion }} - - Hub pod software: See https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{{ include "jupyterhub.chart-version-to-git-ref" .Chart.Version }}/images/hub/requirements.txt + - Hub pod packages: See https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{{ include "jupyterhub.chart-version-to-git-ref" .Chart.Version }}/images/hub/requirements.txt ### Followup links From e735de5634575bf9a5cb8ef88a2d9309fe960b72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Sep 2021 05:18:07 +0000 Subject: [PATCH 038/898] build(deps): bump jupyterhub in /images/singleuser-sample Bumps [jupyterhub](https://github.com/jupyterhub/jupyterhub) from 2.0.0b1 to 2.0.0b2. - [Release notes](https://github.com/jupyterhub/jupyterhub/releases) - [Changelog](https://github.com/jupyterhub/jupyterhub/blob/main/CHECKLIST-Release.md) - [Commits](https://github.com/jupyterhub/jupyterhub/compare/2.0.0b1...2.0.0b2) --- updated-dependencies: - dependency-name: jupyterhub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 7d757b15ef..5f067f8b6b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0b1 +jupyterhub==2.0.0b2 nbgitpuller==1.0.2 From 56989a6a8940d0eb36cf1144af47c5a0671c4e2f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 29 Sep 2021 16:20:05 +0200 Subject: [PATCH 039/898] Test hub.loadRoles access with scoped api token --- .circleci/config.yml | 6 +++++- .github/workflows/test-chart.yaml | 5 ++++- dev-config-local-chart-extra-config.yaml | 12 +++++------- dev-config.yaml | 2 ++ tests/conftest.py | 21 ++++++++++++++++++--- tests/test_hub.py | 16 ++++++++++++++++ 6 files changed, 50 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0675240a9d..798c29877c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,7 +53,11 @@ jobs: - run: command: | export KUBECONFIG="$HOME/.kube/config" - helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml --values dev-config-arm.yaml --wait + helm upgrade --install jupyterhub ./jupyterhub \ + --wait \ + --values dev-config.yaml \ + --values dev-config-arm.yaml \ + --values dev-config-local-chart-extra-config.yaml name: Install local chart - run: diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index b83653a3d5..5392e51c97 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -310,7 +310,10 @@ jobs: - name: "Install local chart" run: | - helm upgrade --install jupyterhub ./jupyterhub --values dev-config.yaml --values dev-config-local-chart-extra-config.yaml ${{ matrix.local-chart-extra-args }} + helm upgrade --install jupyterhub ./jupyterhub \ + --values dev-config.yaml \ + --values dev-config-local-chart-extra-config.yaml \ + ${{ matrix.local-chart-extra-args }} - name: "Await local chart" uses: jupyterhub/action-k8s-await-workloads@v1 diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml index ee8fe0df81..7d7cf34198 100644 --- a/dev-config-local-chart-extra-config.yaml +++ b/dev-config-local-chart-extra-config.yaml @@ -11,13 +11,11 @@ hub: # FIXME: move loadRoles to dev-config.yaml after 2.0.0 is released. loadRoles: - test-role-1: - description: Access to users' information and group membership - scopes: [users, groups] - users: [cyclops, gandalf] - services: [test] - groups: [] - test-role-2-explicit-name: + test-scoped-access: + description: Used to JupyterHub 2.0.0+ RBAC scoped access, currently to the /hub/api/info endpoint via read:hub. + scopes: [read:hub] + services: [test-with-scoped-access] + test-role-with-explicit-name: name: test-role-2 description: Access to users' information and group membership scopes: [users, groups] diff --git a/dev-config.yaml b/dev-config.yaml index 237ca4d683..64478515ee 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -41,6 +41,8 @@ hub: test: admin: true apiToken: give-pytest-control + test-with-scoped-access: + apiToken: give-pytest-scoped-control test-hub-existing-secret: apiToken: dddd4444 test-explicit-name: diff --git a/tests/conftest.py b/tests/conftest.py index 8f95660abe..6f3633f4ea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -28,16 +28,31 @@ def pytest_configure(config): @pytest.fixture(scope="module") -def request_data(): +def admin_api_token(): base_dir = os.path.dirname(os.path.dirname(__file__)) with open(os.path.join(base_dir, "dev-config.yaml")) as f: y = yaml.safe_load(f) token = y["hub"]["services"]["test"]["apiToken"] + return token + + +@pytest.fixture(scope="module") +def scoped_api_token(): + """This token is granted a limited scope""" + base_dir = os.path.dirname(os.path.dirname(__file__)) + with open(os.path.join(base_dir, "dev-config.yaml")) as f: + y = yaml.safe_load(f) + token = y["hub"]["services"]["test-with-scoped-access"]["apiToken"] + return token + + +@pytest.fixture(scope="module") +def request_data(admin_api_token): hub_url = os.environ.get("HUB_URL", "https://local.jovyan.org:30443") return { - "token": token, + "token": admin_api_token, "hub_url": f'{hub_url.rstrip("/")}/hub/api', - "headers": {"Authorization": f"token {token}"}, + "headers": {"Authorization": f"token {admin_api_token}"}, "test_timeout": 60, "request_timeout": 25, } diff --git a/tests/test_hub.py b/tests/test_hub.py index 28b7a5e3cd..d186df0649 100644 --- a/tests/test_hub.py +++ b/tests/test_hub.py @@ -53,6 +53,22 @@ def test_api_info(api_request): assert result["spawner"]["class"] == "kubespawner.spawner.KubeSpawner" +def test_api_info_with_scoped_token(api_request, scoped_api_token): + """ + Test access to the hub api's /info endpoint with an hub api token defined + via hub.services and that is granted the permissions of a role via + hub.loadRoles chart configuration. + + A typical jupyterhub logging response to this test: + + [I 2019-09-25 12:03:12.086 JupyterHub log:174] 200 GET /hub/api/info (test@127.0.0.1) 10.21ms + """ + + print("asking for the hub information using a dedicated token with read:hub scope") + r = api_request.get("/info", headers={"Authorization": f"token {scoped_api_token}"}) + assert r.status_code == 200 + + def test_api_create_and_get_user(api_request, jupyter_user): """ Tests the hub api's /users/:user endpoint, both POST and GET. From d6e99da93df39a1795bcf3521018ced11910d7a9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 14:17:18 +0200 Subject: [PATCH 040/898] ci: remove no longer needed workaround --- .circleci/config.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0675240a9d..cbb9423a19 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,14 +40,9 @@ jobs: pip3 install --no-cache-dir -r dev-requirements.txt name: Install dependencies - # TODO: test 'docker build' instead of buildx - # Ideally we'd use standard docker build since we're building for the native - # platform. Unfortunately the singleuser image can't be built on arm64, and the - # easiest way to skip this image is to use chartpress's --platform option, - # which is only supported with docker-buildx engine - run: command: | - chartpress --builder docker-buildx --platform linux/arm64 + chartpress name: Run chartpress - run: From 54ba0ca1aeab90c588c12315fa1cc8d86dc7200e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 14:32:53 +0200 Subject: [PATCH 041/898] ci: bump circleci to latest k3s ver --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cbb9423a19..4a1a20c469 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: - run: command: >- curl -sfL https://get.k3s.io | - INSTALL_K3S_CHANNEL=v1.20 sh -s - + INSTALL_K3S_CHANNEL=v1.22 sh -s - --disable metrics-server --disable traefik --docker From 449b271acdea8684415b83e6634db4bbd4a56d9d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 16:45:18 +0200 Subject: [PATCH 042/898] Fix proxy pod's liveness/readiness probes to be fully configurable --- jupyterhub/templates/proxy/deployment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml index cd34a495da..d59abd42df 100644 --- a/jupyterhub/templates/proxy/deployment.yaml +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -135,6 +135,8 @@ spec: livenessProbe: initialDelaySeconds: {{ .Values.proxy.chp.livenessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.proxy.chp.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.proxy.chp.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.proxy.chp.livenessProbe.failureThreshold }} httpGet: path: /_chp_healthz {{- if or $manualHTTPS $manualHTTPSwithsecret }} @@ -149,6 +151,8 @@ spec: readinessProbe: initialDelaySeconds: {{ .Values.proxy.chp.readinessProbe.initialDelaySeconds }} periodSeconds: {{ .Values.proxy.chp.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.proxy.chp.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.proxy.chp.readinessProbe.failureThreshold }} httpGet: path: /_chp_healthz {{- if or $manualHTTPS $manualHTTPSwithsecret }} From 6bc95302243f6de3210c96d62e3ab2918547eb20 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 18:52:08 +0200 Subject: [PATCH 043/898] Apply suggestions from code review Co-authored-by: Simon Li --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index d0a56295d1..d3eb771188 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1214,7 +1214,7 @@ properties: about `load_roles`. Note that while JupyterHub's native configuration `load_roles` accepts - a list of role objects, this Helm chart only accept a dictionary where + a list of role objects, this Helm chart only accepts a dictionary where each key represents the name of a role and the value is the actual role object. From 9e1d941f54c68261ede174dcbf1ad54c4873198d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 4 Oct 2021 18:04:21 +0200 Subject: [PATCH 044/898] Add missing default values for proxy pod's probes --- jupyterhub/schema.yaml | 2 +- jupyterhub/values.yaml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index d3eb771188..c65d0b201b 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1075,7 +1075,7 @@ properties: const: true then: description: | - This config option is exactly like the k8s native specification of a + This config option is like the k8s native specification of a container probe, except that it also supports an `enabled` boolean flag. diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 6453f6bbe4..70c59423fe 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -202,11 +202,14 @@ proxy: enabled: true initialDelaySeconds: 60 periodSeconds: 10 + failureThreshold: 30 + timeoutSeconds: 3 readinessProbe: enabled: true initialDelaySeconds: 0 periodSeconds: 2 failureThreshold: 1000 + timeoutSeconds: 1 resources: {} defaultTarget: errorTarget: From 8e8980e00a155c96d0604f8f196eb5dec36dd1d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 19:48:00 +0000 Subject: [PATCH 045/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.28.0 → v2.29.0](https://github.com/asottile/pyupgrade/compare/v2.28.0...v2.29.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71bfcb8182..efbde6fce7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.28.0 + rev: v2.29.0 hooks: - id: pyupgrade args: From 51d6c5266304ba50b4050f789c0d81833d76337a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 4 Oct 2021 22:40:12 +0200 Subject: [PATCH 046/898] ci: don't run twice for pre-commit PRs --- .github/workflows/publish.yml | 1 + .github/workflows/test-chart.yaml | 1 + .github/workflows/test-docker-build.yaml | 1 + .github/workflows/test-docs.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cc1556cdc6..057838dc22 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,6 +19,7 @@ on: - "!.github/workflows/publish.yaml" branches-ignore: - "dependabot/**" + - "pre-commit-ci-update-config" tags: - "**" diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 5392e51c97..1ae3b7b1de 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -21,6 +21,7 @@ on: - "!.github/workflows/test-chart.yaml" branches-ignore: - "dependabot/**" + - "pre-commit-ci-update-config" workflow_dispatch: jobs: diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 89796a69a3..7b39a1bf81 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -19,6 +19,7 @@ on: - ".github/workflows/test-docker-build.yaml" branches-ignore: - "dependabot/**" + - "pre-commit-ci-update-config" workflow_dispatch: jobs: diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 477fad2e50..f3ea2fea7a 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -18,6 +18,7 @@ on: - "**/test-docs.yaml" branches-ignore: - "dependabot/**" + - "pre-commit-ci-update-config" workflow_dispatch: jobs: From 76dfd7c8e25b61d08e1e6f949c347efac535ad05 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Oct 2021 00:04:13 +0000 Subject: [PATCH 047/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/master/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/1.1.0...1.1.1) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 8879714084..57bb5f62c3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -68,7 +68,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2 # via -r requirements.in -jupyterhub-kubespawner==1.1.0 +jupyterhub-kubespawner==1.1.1 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From 880823970a802a6835e50e578e23dbfc9dddc597 Mon Sep 17 00:00:00 2001 From: Joseph Rafferty Date: Fri, 8 Oct 2021 11:50:44 -0500 Subject: [PATCH 048/898] Fixes broken link in chart docs --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index c65d0b201b..c59fb471a5 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -845,7 +845,7 @@ properties: - Anything else you can think of! Since this is usually a multi-line string, you want to format it using YAML's - [| operator](https://yaml.org/spec/1.2/spec.html#id2795688). + [| operator](https://yaml.org/spec/1.2.2/#23-scalars). For example: From 09ff16d522e7a15d74dc9b7f4063c9f3e91005c6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 30 Sep 2021 16:40:13 +0200 Subject: [PATCH 049/898] ci: update shellcheck --- .pre-commit-config-shellcheck.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config-shellcheck.yaml b/.pre-commit-config-shellcheck.yaml index 68a0c352f2..3e0be7f9d7 100644 --- a/.pre-commit-config-shellcheck.yaml +++ b/.pre-commit-config-shellcheck.yaml @@ -1,6 +1,6 @@ # See .pre-commit-config.yaml for more details. repos: - repo: https://github.com/gruntwork-io/pre-commit - rev: v0.1.12 + rev: v0.1.15 hooks: - id: shellcheck From 2d0f5ce6e2fe620db3f2931e8a945d7e852711f4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 1 Oct 2021 07:58:48 +0200 Subject: [PATCH 050/898] Fix shellcheck warnings --- ci/publish | 4 ++++ tools/templates/watch-diff.sh | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ci/publish b/ci/publish index a2b7482090..a045fdd7c3 100755 --- a/ci/publish +++ b/ci/publish @@ -31,10 +31,14 @@ if [[ $GITHUB_REF != refs/tags/* ]]; then PR_OR_HASH=$(git log -1 --pretty=%h-%B | head -n1 | sed 's/^.*\(#[0-9]*\).*/\1/' | sed 's/^\([0-9a-f]*\)-.*/@\1/') LATEST_COMMIT_TITLE=$(git log -1 --pretty=%B | head -n1) EXTRA_MESSAGE="${GITHUB_REPOSITORY}${PR_OR_HASH} ${LATEST_COMMIT_TITLE}" + + # shellcheck disable=SC2086 chartpress $PUBLISH_ARGS --extra-message "${EXTRA_MESSAGE}" else # Setting a tag explicitly enforces a rebuild if this tag had already been # built and we wanted to override it. + + # shellcheck disable=SC2086 chartpress $PUBLISH_ARGS --tag "${GITHUB_REF:10}" fi diff --git a/tools/templates/watch-diff.sh b/tools/templates/watch-diff.sh index b2ee45d37c..358eae1de9 100755 --- a/tools/templates/watch-diff.sh +++ b/tools/templates/watch-diff.sh @@ -17,10 +17,10 @@ rm -rf $TMP_DIFF_DIR mkdir $TMP_DIFF_DIR git init $TMP_DIFF_DIR -helm template jupyterhub --values $HERE_DIR/lint-and-validate-values.yaml --output-dir $TMP_DIFF_DIR +helm template jupyterhub --values "$HERE_DIR/lint-and-validate-values.yaml" --output-dir $TMP_DIFF_DIR # create a point of comparison (cd $TMP_DIFF_DIR && git add . && git commit -m "Comparision point") # watch "git diff" every second (-n1), in color (-c), without watch header (-t) -watch -n1 -ct "helm template jupyterhub --values $HERE_DIR/lint-and-validate-values.yaml --output-dir $TMP_DIFF_DIR > /dev/null && (cd $TMP_DIFF_DIR && git diff --unified=1 --color=always)" +watch -n1 -ct "helm template jupyterhub --values \"$HERE_DIR/lint-and-validate-values.yaml\" --output-dir $TMP_DIFF_DIR > /dev/null && (cd $TMP_DIFF_DIR && git diff --unified=1 --color=always)" From 84a8d4c8990a97bd6e98332d709e388685a4f940 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 1 Oct 2021 08:05:11 +0200 Subject: [PATCH 051/898] Make shellcheck linting be a dedicated job --- .github/workflows/test-chart.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 1ae3b7b1de..9132f7db4e 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -25,6 +25,16 @@ on: workflow_dispatch: jobs: + lint_shell_scripts: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: pre-commit/action@v2.0.3 + name: Run shellcheck linter + with: + extra_args: --config .pre-commit-config-shellcheck.yaml + lint_and_validate_rendered_templates: runs-on: ubuntu-20.04 steps: @@ -36,10 +46,6 @@ jobs: - name: Install dependencies run: pip install chartpress yamllint - - uses: pre-commit/action@v2.0.3 - with: - extra_args: --config .pre-commit-config-shellcheck.yaml - - name: Lint and validate run: tools/templates/lint-and-validate.py From 8f6d6b78cc1509f01ef234431893994614a6c40e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 4 Oct 2021 15:24:57 +0200 Subject: [PATCH 052/898] Apply suggestions from code review Co-authored-by: Simon Li --- .github/workflows/test-chart.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 9132f7db4e..100606d4b3 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -30,10 +30,12 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.3 - name: Run shellcheck linter - with: - extra_args: --config .pre-commit-config-shellcheck.yaml + + - name: Install dependencies + run: pip install pre-commit + + - name: Run shellcheck linter + run: pre-commit run --all --config .pre-commit-config-shellcheck.yaml lint_and_validate_rendered_templates: runs-on: ubuntu-20.04 From 2537863645f84746fdb9b45a167bd5c7369db8e5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Oct 2021 12:56:54 +0200 Subject: [PATCH 053/898] Remove misconfigured chartpress config --- chartpress.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chartpress.yaml b/chartpress.yaml index 3609ebc2da..863beb9d17 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -48,4 +48,7 @@ charts: # singleuser-sample, a primitive user container to start with. singleuser-sample: valuesPath: singleuser.image - PIP_OVERRIDES: jupyterhub==1.4.2 + # Dev: buildArgs can help you try the Helm chart against unreleased + # versions of JupyterHub. + # buildArgs: + # PIP_OVERRIDES: "git+https://github.com/your-username-here/jupyterhub.git" From c9e57f03495d48f6ff30279126e2dad8a27642f9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Oct 2021 12:58:25 +0200 Subject: [PATCH 054/898] Bump to jupyterhub 2.0.0b2 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 6ab7c008b1..74d6bbfd8b 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0b1 +jupyterhub==2.0.0b2 ## Authenticators jupyterhub-firstuseauthenticator diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 57bb5f62c3..5cd8153c0e 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -52,7 +52,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0b1 +jupyterhub==2.0.0b2 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 6aae6c5d7c..864251a082 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0b1 +appVersion: 2.0.0b2 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 73b3d74aaa05367c1fa404979b3d139511333a16 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Oct 2021 16:27:15 +0200 Subject: [PATCH 055/898] Add read:hub scope for idle-culler --- jupyterhub/files/hub/jupyterhub_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index d53aab135d..3dbf0d6950 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -332,6 +332,7 @@ def camelCaseify(s): jupyterhub_idle_culler_role = { "name": "jupyterhub-idle-culler", "scopes": [ + "read:hub", "list:users", "read:users:activity", "servers", From 61b64b39f985a39becf4a59d2b81d8da8db46ede Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Oct 2021 16:27:51 +0200 Subject: [PATCH 056/898] Reduce servers scope to delete:servers scope for idle-culler --- jupyterhub/files/hub/jupyterhub_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 3dbf0d6950..3c1cf522c8 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -335,7 +335,7 @@ def camelCaseify(s): "read:hub", "list:users", "read:users:activity", - "servers", + "delete:servers", # "admin:users", # dynamically added if --cull-users is passed ], # assign the role to a jupyterhub service, so it gains these permissions From 27f9eedbad1a9d27791adb116625023325671db9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Oct 2021 00:03:24 +0000 Subject: [PATCH 057/898] build(deps): bump sqlalchemy-cockroachdb in /images/hub Bumps [sqlalchemy-cockroachdb](https://github.com/cockroachdb/sqlalchemy-cockroachdb) from 1.4.0 to 1.4.1. - [Release notes](https://github.com/cockroachdb/sqlalchemy-cockroachdb/releases) - [Changelog](https://github.com/cockroachdb/sqlalchemy-cockroachdb/blob/master/CHANGES.md) - [Commits](https://github.com/cockroachdb/sqlalchemy-cockroachdb/compare/v1.4.0...v1.4.1) --- updated-dependencies: - dependency-name: sqlalchemy-cockroachdb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5cd8153c0e..326b923e6f 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -178,7 +178,7 @@ sqlalchemy==1.4.23 # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.0 +sqlalchemy-cockroachdb==1.4.1 # via -r requirements.in statsd==3.3.0 # via -r requirements.in From 856a7801667a401652b18652b490935a7afa3eee Mon Sep 17 00:00:00 2001 From: Mike Matera Date: Tue, 12 Oct 2021 21:26:11 -0700 Subject: [PATCH 058/898] Need a comma! --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md index c1e11f9bb8..964e70aadb 100644 --- a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -5,7 +5,7 @@ If you have server hardware available and a small enough user base it's possible to use [Canonical's MicroK8s](https://microk8s.io/) in place of a cloud vendor. ```{warning} -With no ability to scale users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. +With no ability to scale, users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. ``` This guide describes how to configure MicroK8s to work with Zero to Juptyerhub for Kubernetes. From 6d166fa980d83f94064ae052c65341a2adbcd914 Mon Sep 17 00:00:00 2001 From: Mike Matera Date: Tue, 12 Oct 2021 21:26:30 -0700 Subject: [PATCH 059/898] Fix indentation. --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md index 964e70aadb..e458c0326a 100644 --- a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -48,7 +48,7 @@ This guide describes how to configure MicroK8s to work with Zero to Juptyerhub f hosts: - jupyter.myschool.edu letsencrypt: - contactEmail: me@myschool.edu + contactEmail: me@myschool.edu service: loadBalancerIP: 10.0.0.150 ``` From 3d203701a363bc043a4f40f5f3c36bcd6235f62b Mon Sep 17 00:00:00 2001 From: Abdelhakim Qbaich Date: Wed, 13 Oct 2021 13:10:21 -0700 Subject: [PATCH 060/898] auth rework: update forgotten documentation Noticed that this was out of date. --- .../jupyterhub/customizing/user-management.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-management.md b/doc/source/jupyterhub/customizing/user-management.md index 25dec19a62..1818d98068 100644 --- a/doc/source/jupyterhub/customizing/user-management.md +++ b/doc/source/jupyterhub/customizing/user-management.md @@ -93,20 +93,22 @@ perform all these actions. You can specify a list of admin users in your `config.yaml`: ```yaml -auth: - admin: - users: - - adminuser1 - - adminuser2 +hub: + config: + Authenticator: + admin_users: + - adminuser1 + - adminuser2 ``` By default, admins can access user's notebooks. If you wish to disable this, use this in your `config.yaml`: ```yaml -auth: - admin: - access: false +hub: + config: + JupyterHub: + admin_access: false ``` ## Authenticating Users From d77042d1c41c68d8d58dac1c1988afc925b7c46a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 14 Oct 2021 06:10:30 +0200 Subject: [PATCH 061/898] ci: don't re-install yq - its already available --- .github/workflows/test-chart.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 1ae3b7b1de..104bf101bc 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -258,14 +258,6 @@ jobs: run: | helm plugin install https://github.com/databus23/helm-diff - # ref: https://github.com/mikefarah/yq - - name: "(Upgrade) Install yq" - if: matrix.test == 'upgrade' - run: | - VERSION=v4.4.1 - wget https://github.com/mikefarah/yq/releases/download/$VERSION/yq_linux_amd64.tar.gz -qO - | tar xz - sudo mv yq_linux_amd64 /usr/bin/yq - - name: "(Upgrade) Helm diff ${{ matrix.upgrade-from }} chart with local chart" if: matrix.test == 'upgrade' run: | From 916e803c883508f091f1d0b839db1e29b983a0c2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 14 Oct 2021 06:02:39 +0200 Subject: [PATCH 062/898] Add config singleuser.networkTools.resources --- dev-config-local-chart-extra-config.yaml | 8 ++++++++ jupyterhub/files/hub/jupyterhub_config.py | 2 ++ jupyterhub/schema.yaml | 1 + jupyterhub/values.yaml | 1 + 4 files changed, 12 insertions(+) diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml index 7d7cf34198..4c8827e266 100644 --- a/dev-config-local-chart-extra-config.yaml +++ b/dev-config-local-chart-extra-config.yaml @@ -22,3 +22,11 @@ hub: users: [cyclops, gandalf] services: [test] groups: [] + +singleuser: + networkTools: + # FIXME: move resources to dev-config.yaml after 2.0.0 is released. + resources: + requests: + memory: 0 + cpu: 0 diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 3c1cf522c8..8095b21b1c 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -406,6 +406,7 @@ def camelCaseify(s): # Use iptables to block access to cloud metadata by default network_tools_image_name = get_config("singleuser.networkTools.image.name") network_tools_image_tag = get_config("singleuser.networkTools.image.tag") + network_tools_resources = get_config("singleuser.networkTools.resources") ip_block_container = client.V1Container( name="block-cloud-metadata", image=f"{network_tools_image_name}:{network_tools_image_tag}", @@ -423,6 +424,7 @@ def camelCaseify(s): run_as_user=0, capabilities=client.V1Capabilities(add=["NET_ADMIN"]), ), + resources=network_tools_resources, ) c.KubeSpawner.init_containers.append(ip_block_container) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index c59fb471a5..b2ed8ee44e 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2045,6 +2045,7 @@ properties: is set to true. properties: image: *image-spec + resources: *resources-spec # FIXME: name mismatch, named service_account in kubespawner serviceAccountName: type: [string, "null"] diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 70c59423fe..9d337dec19 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -322,6 +322,7 @@ singleuser: tag: "set-by-chartpress" pullPolicy: pullSecrets: [] + resources: {} cloudMetadata: # block set to true will append a privileged initContainer using the # iptables to block the sensitive metadata server at the provided ip. From d3c5c9483a4ab788660b48c033cf6f04021e7bbc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 14 Oct 2021 14:40:44 +0200 Subject: [PATCH 063/898] Bump jupyterhub to 2.0.0b3 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 74d6bbfd8b..921480097f 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0b2 +jupyterhub==2.0.0b3 ## Authenticators jupyterhub-firstuseauthenticator diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 326b923e6f..94ff0b4a6b 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -52,7 +52,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0b2 +jupyterhub==2.0.0b3 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 5f067f8b6b..c6b30d7fd4 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0b2 +jupyterhub==2.0.0b3 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 864251a082..1ece6b1dd4 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0b2 +appVersion: 2.0.0b3 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 2ba648afe8be4b3e08a182e49a736c18ea5c4431 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 14 Oct 2021 14:46:06 +0200 Subject: [PATCH 064/898] Bump jupyterhub-idle-culler --- images/hub/requirements.txt | 2 +- jupyterhub/files/hub/jupyterhub_config.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 94ff0b4a6b..5d3ab96aa4 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -66,7 +66,7 @@ jupyterhub-firstuseauthenticator==0.14.1 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 # via -r requirements.in -jupyterhub-idle-culler==1.2 +jupyterhub-idle-culler==1.2.1 # via -r requirements.in jupyterhub-kubespawner==1.1.1 # via -r requirements.in diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 8095b21b1c..3a251f0632 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -332,7 +332,6 @@ def camelCaseify(s): jupyterhub_idle_culler_role = { "name": "jupyterhub-idle-culler", "scopes": [ - "read:hub", "list:users", "read:users:activity", "delete:servers", From 0aa91e4dd3ed45af05e92379cc12c871a7cf112e Mon Sep 17 00:00:00 2001 From: Timo <6358247+timotk@users.noreply.github.com> Date: Fri, 15 Oct 2021 11:46:26 +0200 Subject: [PATCH 065/898] Fix indentation in local-storage-dir.yaml Otherwise results in error: ``` error parsing local-storage-dir.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key ``` or ``` error: error validating "local-storage-dir.yaml": error validating data: [ValidationError(StorageClass): unknown field "annotations" in io.k8s.api.storage.v1.StorageClass, ValidationError(StorageClass): unknown field "name" in io.k8s.api.storage.v1.StorageClass]; if you choose to ignore these errors, turn validation off with --validate=false ``` --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md index e458c0326a..571088db47 100644 --- a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -74,14 +74,14 @@ This guide describes how to configure MicroK8s to work with Zero to Juptyerhub f apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: - name: local-storage-dir - annotations: + name: local-storage-dir + annotations: storageclass.kubernetes.io/is-default-class: "true" openebs.io/cas-type: local cas.openebs.io/config: | - - name: StorageType + - name: StorageType value: hostpath - - name: BasePath + - name: BasePath value: /path/to/your/storage provisioner: openebs.io/local reclaimPolicy: Delete From 7fdae09cfa7ee1b0a27cf8e81dcd53eb8beceeaa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 19:24:04 +0000 Subject: [PATCH 066/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/lovesegfault/beautysh: v6.1.0 → v6.2.1](https://github.com/lovesegfault/beautysh/compare/v6.1.0...v6.2.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index efbde6fce7..a828c6b5e8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,7 @@ repos: # Autoformat: Bash scripts - repo: https://github.com/lovesegfault/beautysh - rev: v6.1.0 + rev: v6.2.1 hooks: - id: beautysh From 1e6c3f6f5846f0a582fa71b9b9b80f0740149e2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Oct 2021 00:08:08 +0000 Subject: [PATCH 067/898] build(deps): bump jupyterhub-nativeauthenticator in /images/hub Bumps [jupyterhub-nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) from 0.0.7 to 1.0.1. - [Release notes](https://github.com/jupyterhub/nativeauthenticator/releases) - [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nativeauthenticator/compare/0.0.7...1.0.1) --- updated-dependencies: - dependency-name: jupyterhub-nativeauthenticator dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5d3ab96aa4..772b9fec44 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -74,7 +74,7 @@ jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in jupyterhub-ltiauthenticator==1.2.0 # via -r requirements.in -jupyterhub-nativeauthenticator==0.0.7 +jupyterhub-nativeauthenticator==1.0.1 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in From ce3d3c8c79bdc9be69ce876ee360055f6e0293b0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 19 Oct 2021 09:55:47 +0200 Subject: [PATCH 068/898] idle-culler: fix permissions to include read:servers --- jupyterhub/files/hub/jupyterhub_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 3a251f0632..49050ef423 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -334,6 +334,7 @@ def camelCaseify(s): "scopes": [ "list:users", "read:users:activity", + "read:servers", "delete:servers", # "admin:users", # dynamically added if --cull-users is passed ], From 03e4a766b7fbb031f9fc292958ae8d6c03af6137 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Oct 2021 00:09:34 +0000 Subject: [PATCH 069/898] build(deps): bump jupyterhub-nativeauthenticator in /images/hub Bumps [jupyterhub-nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) from 1.0.1 to 1.0.5. - [Release notes](https://github.com/jupyterhub/nativeauthenticator/releases) - [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nativeauthenticator/compare/1.0.1...1.0.5) --- updated-dependencies: - dependency-name: jupyterhub-nativeauthenticator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 772b9fec44..ad187336b1 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -74,7 +74,7 @@ jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in jupyterhub-ltiauthenticator==1.2.0 # via -r requirements.in -jupyterhub-nativeauthenticator==1.0.1 +jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in From 5e74a78b6e65bf4c58ae10f6853ed720a4df5ca2 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 21 Oct 2021 14:20:04 +0200 Subject: [PATCH 070/898] leave default cmd to the image Removes inconsistent defaults across z2jh, kubespawner, image. KubeSpawner already sets `cmd = None` by default, which means using the CMD from the image. With JupyterHub 2.0, that means lab by default, running on jupyter-server. --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 9d337dec19..408e48da6e 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -387,7 +387,7 @@ singleuser: extraResource: limits: {} guarantees: {} - cmd: jupyterhub-singleuser + cmd: defaultUrl: extraPodConfig: {} profileList: [] From 9170bc84fee9ebf409d6e85ecbc5ad320d41f3fe Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 21 Oct 2021 14:41:46 +0200 Subject: [PATCH 071/898] document setting singleuser.cmd Now that we respect the image by default, more custom images will need `singleuser.cmd` to be set. --- .../customizing/user-environment.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index e23abec75a..99854a62e6 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -242,6 +242,12 @@ FROM jupyter/minimal-notebook:177037d09156 # install additional package... RUN pip install --no-cache-dir astropy + +# set the default command of the image, +# if the parent image will not launch a jupyterhub singleuser server. +# The JupyterHub "Docker stacks" do not need to be overridden. +# Set either here or in `singleuser.cmd` in your values.yaml +# CMD ["jupyterhub-singleuser"] ``` ```{note} @@ -501,3 +507,37 @@ using the Kubespawner `profile_form_template` configuration. See the [Kubespawner configuration reference](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) for more information. ``` + +(set-cmd)= + +## Set command to launch + +Ultimately, a single-user server should launch the `jupyterhub-singleuser` command. +However, an image may have a custom CMD that does this, +with some preparation steps, or adding additional command-line arguments, +or launching a custom wrapper command, etc. + +:::{note} +If you have environment preparation at startup in your image, +this is best done in the ENTRYPOINT of the image, +and not in the CMD, so that overriding the command does not skip your preparation. +::: + +By default, zero-to-jupyterhub will launch the default CMD that is specified in your chosen image, +respecting any startup customization that image may have. +If the image doesn't launch `jupyterhub-singleuser` by default, +you will additionally need to specify `singleuser.cmd` +in your `values.yaml` as the command to launch, +so that it ultimately launches `jupyterhub-singleuser`. +The simplest version: + +```yaml +singleuser: + cmd: jupyterhub-singleuser +``` + +:::{versionchanged} 2.0 +Prior to 2.0, the default behavior of zero-to-jupyterhub was to launch `jupyterhub-singleuser` explicitly, +ignoring what was in the image. +The default command is now whatever the image runs by default. +::: From 7c11beb931e8657299b3391e50a7b3b2f66fffe8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Oct 2021 00:09:09 +0000 Subject: [PATCH 072/898] build(deps): bump sqlalchemy-cockroachdb in /images/hub Bumps [sqlalchemy-cockroachdb](https://github.com/cockroachdb/sqlalchemy-cockroachdb) from 1.4.1 to 1.4.2. - [Release notes](https://github.com/cockroachdb/sqlalchemy-cockroachdb/releases) - [Changelog](https://github.com/cockroachdb/sqlalchemy-cockroachdb/blob/master/CHANGES.md) - [Commits](https://github.com/cockroachdb/sqlalchemy-cockroachdb/compare/v1.4.1...v1.4.2) --- updated-dependencies: - dependency-name: sqlalchemy-cockroachdb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index ad187336b1..203ec5b189 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -178,7 +178,7 @@ sqlalchemy==1.4.23 # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.1 +sqlalchemy-cockroachdb==1.4.2 # via -r requirements.in statsd==3.3.0 # via -r requirements.in From 28efde1bfff520092873ce90ae5210d61c3bbd54 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 26 Oct 2021 01:03:18 +0000 Subject: [PATCH 073/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 49e801468d..4fa4f8f42c 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-09-17_01:10:00 +# VULN_SCAN_TIME=2021-10-26_01:03:15 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 581f37ece58aa1e27ed8dfe14ddad51056f8ac85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Oct 2021 05:04:00 +0000 Subject: [PATCH 074/898] build(deps): bump aquasecurity/trivy-action from 0.0.20 to 0.0.22 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.0.20 to 0.0.22. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/8eccb5539730451af599c84f444c6d6cf0fc2bb0...1ccef265f594a7555a720f623a461a3d69b45bf7) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 8f25f20d89..fef3adb241 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@8eccb5539730451af599c84f444c6d6cf0fc2bb0 + uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8eccb5539730451af599c84f444c6d6cf0fc2bb0 + uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8eccb5539730451af599c84f444c6d6cf0fc2bb0 + uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 with: image-ref: rebuilt-image format: table From 36dbf1cefa3f419c578e9e141106294ed9684e08 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 28 Oct 2021 09:52:53 +0200 Subject: [PATCH 075/898] match config load priority for extraFiles and extraConfig Before (priority order): 1. extraConfig 2. hub.config 3. extraFiles After: 1. extraConfig 2. extraFiles 3. hub.config --- jupyterhub/files/hub/jupyterhub_config.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 49050ef423..f7733fefa3 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -434,17 +434,6 @@ def camelCaseify(s): c.JupyterHub.log_level = "DEBUG" c.Spawner.debug = True -# load /usr/local/etc/jupyterhub/jupyterhub_config.d config files -config_dir = "/usr/local/etc/jupyterhub/jupyterhub_config.d" -if os.path.isdir(config_dir): - for file_path in sorted(glob.glob(f"{config_dir}/*.py")): - file_name = os.path.basename(file_path) - print(f"Loading {config_dir} config: {file_name}") - with open(file_path) as f: - file_content = f.read() - # compiling makes debugging easier: https://stackoverflow.com/a/437857 - exec(compile(source=file_content, filename=file_name, mode="exec")) - # load potentially seeded secrets # # NOTE: ConfigurableHTTPProxy.auth_token is set through an environment variable @@ -467,6 +456,17 @@ def camelCaseify(s): cfg.pop("keys", None) c[app].update(cfg) +# load /usr/local/etc/jupyterhub/jupyterhub_config.d config files +config_dir = "/usr/local/etc/jupyterhub/jupyterhub_config.d" +if os.path.isdir(config_dir): + for file_path in sorted(glob.glob(f"{config_dir}/*.py")): + file_name = os.path.basename(file_path) + print(f"Loading {config_dir} config: {file_name}") + with open(file_path) as f: + file_content = f.read() + # compiling makes debugging easier: https://stackoverflow.com/a/437857 + exec(compile(source=file_content, filename=file_name, mode="exec")) + # execute hub.extraConfig entries for key, config_py in sorted(get_config("hub.extraConfig", {}).items()): print(f"Loading extra config: {key}") From 4538202b9bb45400cfa684f07865cd221ba1b80b Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 28 Oct 2021 13:37:24 +0200 Subject: [PATCH 076/898] bump jupyterhub-firstuseauthenticator to 1.0 fixes issue with username normalization --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 921480097f..2414b63f74 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -8,7 +8,7 @@ jupyterhub==2.0.0b3 ## Authenticators -jupyterhub-firstuseauthenticator +jupyterhub-firstuseauthenticator>=1 jupyterhub-hmacauthenticator jupyterhub-ldapauthenticator jupyterhub-ltiauthenticator diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 203ec5b189..7f36b4a702 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -62,7 +62,7 @@ jupyterhub==2.0.0b3 # jupyterhub-nativeauthenticator # nullauthenticator # oauthenticator -jupyterhub-firstuseauthenticator==0.14.1 +jupyterhub-firstuseauthenticator==1.0.0 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 # via -r requirements.in From 385c3d75a4cdb5c67b6001a8d9b675e54aafae2f Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 28 Oct 2021 13:41:03 +0200 Subject: [PATCH 077/898] Changelog entry for 1.1.4 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9dc0f3dcb..580bd24044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ Here you can find upgrade changes in between releases and upgrade instructions. ## [1.1] +### [1.1.4] - 2021-10-28 + +Security release! 1.1.4 release fixes a [critical security vulnerability][ghsa-5xvc-vgmp-jgc3] in jupyterhub-firstuse authenticator. +If you are not using firstuseauthenticator, you are not affected. + +[ghsa-5xvc-vgmp-jgc3]: https://github.com/jupyterhub/firstuseauthenticator/security/advisories/GHSA-5xvc-vgmp-jgc3 + ### [1.1.3] - 2021-08-25 ## Maintenance and upkeep improvements From 7a36da20f47bcb8cbb8f510b2971134dd5c45c1e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 31 Oct 2021 09:57:48 +0100 Subject: [PATCH 078/898] ci: vuln-scan, run on PR changes to workflow file --- .github/workflows/vuln-scan.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index fef3adb241..b8ad0ba1e2 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -9,6 +9,9 @@ name: Vuln. scan on: + pull_request: + paths: + - ".github/workflows/vuln-scan.yaml" schedule: # At 00:00 - https://crontab.guru - cron: "0 0 * * *" @@ -186,13 +189,15 @@ jobs: file_to_update="images/${{ matrix.image_ref }}/Dockerfile" sed --in-place "s/\(#.*VULN_SCAN_TIME=\)\(.*\)/\1${value_to_set}/" "$file_to_update" + git --no-pager diff --color=always + # The create-pull-request action is smart enough to only create/update a # PR if there is a change to anything not .gitignored. A change will be # made only if the analyze steps outputted hash is changed. # # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR - if: steps.analyze.outputs.proceed == 'yes' + if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' uses: peter-evans/create-pull-request@7380612b49221684fefa025244f2ef4008ae50ad with: token: "${{ secrets.GITHUB_TOKEN }}" From 8b489b0c4c051a0bf6d343140e456150a6240a3a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 31 Oct 2021 09:58:53 +0100 Subject: [PATCH 079/898] ci: vuln-scan, don't accept failures on singleuser-image --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index b8ad0ba1e2..0cca0243b5 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -40,7 +40,7 @@ jobs: - image_ref: image-awaiter accept_failure: false - image_ref: singleuser-sample - accept_failure: true + accept_failure: false steps: - uses: actions/checkout@v2 From 92610ba95182d9f3bad8c0bf85b8e7356851e137 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 31 Oct 2021 09:59:08 +0100 Subject: [PATCH 080/898] ci: vuln-scan, adjust to trivy's changed schema in emitted json --- .github/workflows/vuln-scan.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0cca0243b5..16fce61241 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -123,7 +123,7 @@ jobs: json_to_misc() { # Count vulnerabilities - VULNERABILITY_COUNT="$(cat tmp/scan_$1.json | jq -r '[.[].Vulnerabilities | select(type != null)] | add | select(. != null) | length')" + VULNERABILITY_COUNT="$(cat tmp/scan_$1.json | jq -r '[.Results[].Vulnerabilities | select(type != null)] | add | select(. != null) | length')" echo "VULNERABILITY_COUNT_$1=$VULNERABILITY_COUNT" >> $GITHUB_ENV # Construct a markdown summary @@ -132,7 +132,7 @@ jobs: else echo "Target | Vuln. ID | Package Name | Installed v. | Fixed v." >> tmp/md_summary_$1.md echo "-|-|-|-|-" >> tmp/md_summary_$1.md - cat tmp/scan_$1.json | jq -r '.[] | select(.Vulnerabilities != null) | .Type + " | " + (.Vulnerabilities[] | .VulnerabilityID + " | " + .PkgName + " | " + .InstalledVersion + " | " + .FixedVersion)' | sort >> tmp/md_summary_$1.md + cat tmp/scan_$1.json | jq -r '.Results[] | select(.Vulnerabilities != null) | .Type + " | " + (.Vulnerabilities[] | .VulnerabilityID + " | " + .PkgName + " | " + .InstalledVersion + " | " + .FixedVersion)' | sort >> tmp/md_summary_$1.md fi # Use hack to set a multiline string output From ca65a37ec465f328178c17ad0906956f5d8792fc Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sun, 31 Oct 2021 09:18:11 +0000 Subject: [PATCH 081/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index f5f16fd825..35872dce58 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-09-28_01:04:16 +# VULN_SCAN_TIME=2021-10-31_09:18:08 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 89c548a7a07dedc791b9a8dc4fc6487dad28418a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 31 Oct 2021 15:47:41 +0100 Subject: [PATCH 082/898] Bump to JupyterHub 2.0.0rc2 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 2414b63f74..4003977408 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0b3 +jupyterhub==2.0.0rc2 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7f36b4a702..c5a203fffc 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -52,7 +52,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0b3 +jupyterhub==2.0.0rc2 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index c6b30d7fd4..3e4b6c4b2a 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0b3 +jupyterhub==2.0.0rc2 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 1ece6b1dd4..0b9cb911dd 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0b3 +appVersion: 2.0.0rc2 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 7d4ceb86f322f77b313e301333bc9052fb648ec0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Nov 2021 20:26:02 +0000 Subject: [PATCH 083/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.9b0 → 21.10b0](https://github.com/psf/black/compare/21.9b0...21.10b0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a828c6b5e8..1ed583ae4a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 21.9b0 + rev: 21.10b0 hooks: - id: black args: [--target-version=py36] From 49fbdda257236eb0746761578275017e47569f7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Nov 2021 13:24:15 +0000 Subject: [PATCH 084/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 1.1.1 to 1.1.2. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/1.1.1...1.1.2) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index c5a203fffc..cc3cc72406 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -68,7 +68,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==1.1.1 +jupyterhub-kubespawner==1.1.2 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -162,8 +162,6 @@ rsa==4.7.2 # via google-auth ruamel.yaml==0.17.16 # via jupyter-telemetry -ruamel.yaml.clib==0.2.6 - # via ruamel.yaml six==1.16.0 # via # bcrypt From 3340bfd4d97b4a44000f85cfb0d8ddc9ffb9d1d7 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 4 Nov 2021 01:03:43 +0000 Subject: [PATCH 085/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 35872dce58..1a8056f1ed 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-10-31_09:18:08 +# VULN_SCAN_TIME=2021-11-04_01:03:41 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 048c20a2916bcca3c194b1bad8753e387b58224c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Nov 2021 05:04:22 +0000 Subject: [PATCH 086/898] build(deps): bump peter-evans/create-pull-request from 3.10.1 to 3.11.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.10.1 to 3.11.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/7380612b49221684fefa025244f2ef4008ae50ad...67df31e08a133c6a77008b89689677067fef169e) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 16fce61241..7ea66a3e15 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -198,7 +198,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@7380612b49221684fefa025244f2ef4008ae50ad + uses: peter-evans/create-pull-request@67df31e08a133c6a77008b89689677067fef169e with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From d2a364378774bb7562f2549d7131fe742fe065fa Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 4 Nov 2021 15:06:39 +0100 Subject: [PATCH 087/898] docs: fix failure to show correct version --- doc/source/conf.py | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index df6109499c..46beb7138a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -35,21 +35,6 @@ def setup(app): # -- Referencable variables -------------------------------------------------- -def _get_latest_tag(): - """Get the latest tag on a commit in branch or return None.""" - try: - # If the git command output is my-tag-14-g0aed65e, - # then the return value will become my-tag. - return ( - subprocess.check_output(["git", "describe", "--tags", "--long"]) - .decode("utf-8") - .strip() - .rsplit("-", maxsplit=2)[0] - ) - except subprocess.CalledProcessError: - return None - - def _get_git_ref_from_chartpress_based_version(version): """ Get a git ref from a chartpress set version of format like @@ -71,28 +56,19 @@ def _get_git_ref_from_chartpress_based_version(version): chart = yaml.safe_load(f) subprocess.run(["chartpress", "--reset"], cwd=os.path.abspath("../..")) -latest_tag = _get_latest_tag() -chart_version = chart["version"] -chart_version_git_ref = _get_git_ref_from_chartpress_based_version(chart_version) -jupyterhub_version = chart["appVersion"] -# FIXME: kubeVersion contain >=, but by having > in the string we substitute we -# run into this issue: -# https://github.com/executablebooks/MyST-Parser/issues/282 -kube_version = chart["kubeVersion"].split("-", 1)[0][2:] -helm_version = "3.5" # minimum helm cli version - # These substitution variables only work in markdown contexts, and does not work # within links etc. Reference using {{ variable_name }} # # myst_substitutions ref: https://myst-parser.readthedocs.io/en/latest/using/syntax-optional.html#substitutions-with-jinja2 myst_substitutions = { - "latest_tag": latest_tag, - "chart_version": chart_version, - "chart_version_git_ref": chart_version_git_ref, - "jupyterhub_version": jupyterhub_version, - "kube_version": kube_version, - "helm_version": helm_version, - "requirements": f"[hub/images/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{chart_version_git_ref}/images/hub/requirements.txt)", + "chart_version": chart["version"], + "jupyterhub_version": chart["appVersion"], + # FIXME: kubeVersion contain >=, but by having > in the string we substitute + # we run into this issue: + # https://github.com/executablebooks/MyST-Parser/issues/282 + "kube_version": chart["kubeVersion"].split("-", 1)[0][2:], + "helm_version": "3.5", + "requirements": f"[hub/images/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{_get_git_ref_from_chartpress_based_version(chart['version'])}/images/hub/requirements.txt)", } From 70c72420d5d0cfdc57db3c1df1027ac15addcb3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Nov 2021 05:04:16 +0000 Subject: [PATCH 088/898] build(deps): bump jupyterhub in /images/singleuser-sample Bumps [jupyterhub](https://github.com/jupyterhub/jupyterhub) from 2.0.0rc2 to 2.0.0rc3. - [Release notes](https://github.com/jupyterhub/jupyterhub/releases) - [Changelog](https://github.com/jupyterhub/jupyterhub/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/jupyterhub/compare/2.0.0rc2...2.0.0rc3) --- updated-dependencies: - dependency-name: jupyterhub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 3e4b6c4b2a..bd77c9e382 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0rc2 +jupyterhub==2.0.0rc3 nbgitpuller==1.0.2 From 44cbc4c30b318cc76e9483d862c3803c94ad27de Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 5 Nov 2021 14:28:25 +0100 Subject: [PATCH 089/898] Bump JupyterHub to 2.0.0rc3 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 4003977408..81a1fe0db4 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0rc2 +jupyterhub==2.0.0rc3 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index cc3cc72406..d86dc204f2 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -52,7 +52,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0rc2 +jupyterhub==2.0.0rc3 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 0b9cb911dd..b7dd788d82 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0rc2 +appVersion: 2.0.0rc3 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From cb77b0ca042eb9f4b87b8a8b309b34932bf14b6d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 5 Nov 2021 17:25:43 +0100 Subject: [PATCH 090/898] docs: fix syntax errors with directives --- .../jupyterhub/customizing/user-environment.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 99854a62e6..213ec8ecd9 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -138,9 +138,9 @@ That way, your choice will be preserved across upgrades. ## Use JupyterLab by default -:::{note} +```{note} This is the default in JupyterHub 2.0 and Helm chart 2.0. -::: +``` You can choose JupyterLab as the default UI with the following config in your {term}`config.yaml`: @@ -161,7 +161,7 @@ singleuser: JUPYERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" ``` -````{note} +```{note} You need the `jupyterlab` package (installable via `pip` or `conda`) for this to work. All images in the [jupyter/docker-stacks repository](https://github.com/jupyter/docker-stacks/) come pre-installed with it. ``` @@ -170,9 +170,9 @@ for this to work. All images in the [jupyter/docker-stacks repository](https://g ### Use classic notebook by default -:::{note} +```{note} This is the default in JupyterHub 1.x and helm chart 1.x. -::: +``` If you aren't ready to upgrade to JupyterLab, especially for those who depend on custom notebook extensions without an equivalent in JupyterLab, @@ -183,7 +183,7 @@ you can always stick with the legacy notebook server (`jupyter notebook`): singleuser: extraEnv: JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" -```` +``` This will start the exact same server and UI as before. @@ -536,8 +536,8 @@ singleuser: cmd: jupyterhub-singleuser ``` -:::{versionchanged} 2.0 +```{versionchanged} 2.0 Prior to 2.0, the default behavior of zero-to-jupyterhub was to launch `jupyterhub-singleuser` explicitly, ignoring what was in the image. The default command is now whatever the image runs by default. -::: +``` From 1214e14642a8acc47428e708f5d9d8845a85db47 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Wed, 10 Nov 2021 01:04:27 +0000 Subject: [PATCH 091/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 4fa4f8f42c..8466c5b4aa 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-10-26_01:03:15 +# VULN_SCAN_TIME=2021-11-10_01:04:25 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 5753ea4675e028ce3b7c84fa86ebfeb1060c7be1 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 4 Nov 2021 14:12:20 +0100 Subject: [PATCH 092/898] Add changelog for 1.2.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 580bd24044..687ecc8870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ Here you can find upgrade changes in between releases and upgrade instructions. ## UNRELEASED +## 1.2 + +### 1.2.0 - 2021-11-04 + +Security release! Updates JupyterHub to 1.5 to address a [moderate security vulnerability][ghsa-cw7p-q79f-m2v7] +affecting JupyterLab users, +where logout may not always fully clear credentials from the browser if multiple sessions are open at the time. + +[ghsa-cw7p-q79f-m2v7]: https://github.com/jupyterhub/jupyterhub/security/advisories/GHSA-cw7p-q79f-m2v7 + +A few small features are backported from the upcoming 2.0 release as well. +See [the release notes][juptyerhub-1.5-changelog] for more. + +[juptyerhub-1.5-changelog]: https://jupyterhub.readthedocs.io/en/1.5.0/changelog.html#id1 + +Because the vulnerability is in the single-user environment, +you can get the fix in existing deployments by upgrading JupyterHub to 1.5 in your _user_ environment +without updating the rest of your chart. + +Similarly, upgrading the chart without also upgrading JupyterHub to 1.5 in your user environment **will not** fix the vulnerability. + +JupyterHub 1.5 in the user environment is fully compatible with a Hub running 1.4, and _vice versa_. + ## [1.1] ### [1.1.4] - 2021-10-28 From 739f4b4787a26e6e9b6bfb0c8c23811c05e10e62 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sat, 13 Nov 2021 01:01:41 +0000 Subject: [PATCH 093/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 87711ee7eb..7773ba1aa0 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2021-08-28_00:55:50 +# VULN_SCAN_TIME=2021-11-13_01:01:39 RUN apk add --no-cache iptables From 12898c9eaa5733b30109aebdabe9e2d641dbdfcb Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sun, 14 Nov 2021 01:05:26 +0000 Subject: [PATCH 094/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 601c305903..d21048699e 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2021-08-28_00:56:18 +# VULN_SCAN_TIME=2021-11-14_01:05:25 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From cdaedb9f45297f14cdb00242d212bb9d9154bcd5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Nov 2021 00:21:46 +0000 Subject: [PATCH 095/898] build(deps): bump psycopg2-binary from 2.9.1 to 2.9.2 in /images/hub Bumps [psycopg2-binary](https://github.com/psycopg/psycopg2) from 2.9.1 to 2.9.2. - [Release notes](https://github.com/psycopg/psycopg2/releases) - [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS) - [Commits](https://github.com/psycopg/psycopg2/commits) --- updated-dependencies: - dependency-name: psycopg2-binary dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index d86dc204f2..c6f5564684 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -108,7 +108,7 @@ pamela==1.0.0 # via jupyterhub prometheus-client==0.11.0 # via jupyterhub -psycopg2-binary==2.9.1 +psycopg2-binary==2.9.2 # via -r requirements.in py-spy==0.3.10 # via -r requirements.in From 4898e254b7ff59d0f00a41dc78a3c0937f37a905 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Nov 2021 05:04:57 +0000 Subject: [PATCH 096/898] build(deps): bump aquasecurity/trivy-action from 0.0.22 to 0.1.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.0.22 to 0.1.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/1ccef265f594a7555a720f623a461a3d69b45bf7...2a2157eb22c08c9a1fac99263430307b8d1bc7a2) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 7ea66a3e15..21b0500fa8 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -82,7 +82,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 + uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -105,7 +105,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 + uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -164,7 +164,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@1ccef265f594a7555a720f623a461a3d69b45bf7 + uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 with: image-ref: rebuilt-image format: table From 35a49e3e45ab87134f73c5f59f52f670c4acab49 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Nov 2021 18:30:56 +0000 Subject: [PATCH 097/898] build(deps): bump py-spy from 0.3.10 to 0.3.11 in /images/hub Bumps [py-spy](https://github.com/benfred/py-spy) from 0.3.10 to 0.3.11. - [Release notes](https://github.com/benfred/py-spy/releases) - [Changelog](https://github.com/benfred/py-spy/blob/master/CHANGELOG.md) - [Commits](https://github.com/benfred/py-spy/compare/v0.3.10...v0.3.11) --- updated-dependencies: - dependency-name: py-spy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index c6f5564684..d3076b275d 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -110,7 +110,7 @@ prometheus-client==0.11.0 # via jupyterhub psycopg2-binary==2.9.2 # via -r requirements.in -py-spy==0.3.10 +py-spy==0.3.11 # via -r requirements.in pyasn1==0.4.8 # via From 1b13460cbe0bef8784887d69962d7106dc9fc5c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Nov 2021 00:06:41 +0000 Subject: [PATCH 098/898] build(deps): bump jupyterhub-ltiauthenticator in /images/hub Bumps [jupyterhub-ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) from 1.2.0 to 1.3.0. - [Release notes](https://github.com/jupyterhub/ltiauthenticator/releases) - [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/ltiauthenticator/compare/1.2.0...1.3.0) --- updated-dependencies: - dependency-name: jupyterhub-ltiauthenticator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index d3076b275d..f382d9a89e 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -31,13 +31,20 @@ cffi==1.14.6 charset-normalizer==2.0.6 # via requests cryptography==3.4.8 - # via pyopenssl + # via + # josepy + # jwcrypto + # pyopenssl +deprecated==1.2.13 + # via jwcrypto entrypoints==0.3 # via jupyterhub escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator +future==0.18.2 + # via pyjwkest google-auth==2.1.0 # via kubernetes greenlet==1.1.1 @@ -48,6 +55,8 @@ jinja2==3.0.1 # via # jupyterhub # jupyterhub-kubespawner +josepy==1.10.0 + # via jupyterhub-ltiauthenticator jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 @@ -72,12 +81,14 @@ jupyterhub-kubespawner==1.1.2 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.2.0 +jupyterhub-ltiauthenticator==1.3.0 # via -r requirements.in jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in +jwcrypto==1.0 + # via jupyterhub-ltiauthenticator kubernetes==18.20.0 # via jupyterhub-kubespawner ldap3==2.9.1 @@ -93,7 +104,9 @@ mwoauth==0.3.7 nullauthenticator==1.0.0 # via -r requirements.in oauthenticator==14.2.0 - # via -r requirements.in + # via + # -r requirements.in + # jupyterhub-ltiauthenticator oauthlib==3.1.1 # via # jupyterhub @@ -106,6 +119,8 @@ packaging==21.0 # via jupyterhub pamela==1.0.0 # via jupyterhub +pem==21.2.0 + # via jupyterhub-ltiauthenticator prometheus-client==0.11.0 # via jupyterhub psycopg2-binary==2.9.2 @@ -121,16 +136,25 @@ pyasn1-modules==0.2.8 # via google-auth pycparser==2.20 # via cffi +pycryptodome==3.11.0 + # via jupyterhub-ltiauthenticator +pycryptodomex==3.11.0 + # via pyjwkest pycurl==7.44.1 # via -r requirements.in +pyjwkest==1.4.2 + # via jupyterhub-ltiauthenticator pyjwt==1.7.1 # via # -r requirements.in + # jupyterhub-ltiauthenticator # mwoauth pymysql==1.0.2 # via -r requirements.in pyopenssl==20.0.1 - # via certipy + # via + # certipy + # josepy pyparsing==2.4.7 # via packaging pyrsistent==0.18.0 @@ -153,6 +177,7 @@ requests==2.26.0 # jupyterhub # kubernetes # mwoauth + # pyjwkest # requests-oauthlib requests-oauthlib==1.3.0 # via @@ -169,6 +194,7 @@ six==1.16.0 # kubernetes # mwoauth # onetimepass + # pyjwkest # pyopenssl # python-dateutil sqlalchemy==1.4.23 @@ -199,6 +225,8 @@ urllib3==1.26.6 # requests websocket-client==1.2.1 # via kubernetes +wrapt==1.13.3 + # via deprecated # The following packages are considered to be unsafe in a requirements file: # setuptools From 08d1fc748d736014b78075cc6b7513efd9f78c7d Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Tue, 16 Nov 2021 15:10:03 -0600 Subject: [PATCH 099/898] Fix docs typo Following [1] and other instances in the same doc this fixes the typo on the env var to set to use jupyter-server in the singleuser server pod. Also make the retro example set the env var value in a quotes since it's a string. [1] https://jupyterhub.readthedocs.io/en/stable/reference/config-user-env.html#switching-to-jupyter-server --- doc/source/jupyterhub/customizing/user-environment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 213ec8ecd9..1a43db3492 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -148,7 +148,7 @@ You can choose JupyterLab as the default UI with the following config in your {t singleuser: defaultUrl: "/lab" extraEnv: - JUPYERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" + JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" ``` You can also make JupyterLab the default UI _without_ upgrading to the newer server implementation. @@ -158,7 +158,7 @@ This may help users who need to stick to the legacy UI with extensions that may singleuser: defaultUrl: "/lab" extraEnv: - JUPYERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" + JUPYTERHUB_SINGLEUSER_APP: "notebook.notebookapp.NotebookApp" ``` ```{note} @@ -217,7 +217,7 @@ To install such an extension: singleuser: defaultUrl: /retro/ extraEnv: - JUPYTERHUB_SINGLEUSER_APP: jupyter_server.serverapp.ServerApp + JUPYTERHUB_SINGLEUSER_APP: "jupyter_server.serverapp.ServerApp" ``` (custom-docker-image)= From db11878ea13efbb94abc09a42d1684ee2ab265c2 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Tue, 16 Nov 2021 15:16:17 -0600 Subject: [PATCH 100/898] Fix broken link to k8s nodeport docs --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index b2ed8ee44e..ab1562cac3 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1473,7 +1473,7 @@ properties: Object to set NodePorts to expose the service on for http and https. See [the Kubernetes - documentation](https://kubernetes.io/docs/concepts/services-networking/service/#nodeport) + documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) for more details about NodePorts. properties: http: From 0c9195757b0ada77593d87a5726cff6298d26557 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Nov 2021 05:03:28 +0000 Subject: [PATCH 101/898] build(deps): bump jupyterhub in /images/singleuser-sample Bumps [jupyterhub](https://github.com/jupyterhub/jupyterhub) from 2.0.0rc3 to 2.0.0rc4. - [Release notes](https://github.com/jupyterhub/jupyterhub/releases) - [Changelog](https://github.com/jupyterhub/jupyterhub/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/jupyterhub/compare/2.0.0rc3...2.0.0rc4) --- updated-dependencies: - dependency-name: jupyterhub dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index bd77c9e382..529f7b8d8b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0rc3 +jupyterhub==2.0.0rc4 nbgitpuller==1.0.2 From 8ec2834387a5b88ea3342a0a58ce661356a3d4d9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 19 Nov 2021 18:24:21 +0100 Subject: [PATCH 102/898] bump jupyterhub to 2.0.0rc4 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 81a1fe0db4..0a72c756a6 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0rc3 +jupyterhub==2.0.0rc4 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index f382d9a89e..7707f5ed34 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0rc3 +jupyterhub==2.0.0rc4 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index b7dd788d82..92c9a19445 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0rc3 +appVersion: 2.0.0rc4 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From d9b0a64603e46640135f7d6c6492ff7eb866e5d1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 20:50:45 +0000 Subject: [PATCH 103/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.29.0 → v2.29.1](https://github.com/asottile/pyupgrade/compare/v2.29.0...v2.29.1) - [github.com/psf/black: 21.10b0 → 21.11b1](https://github.com/psf/black/compare/21.10b0...21.11b1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ed583ae4a..e3dd0afe81 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.29.1 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 21.10b0 + rev: 21.11b1 hooks: - id: black args: [--target-version=py36] From ee7a23c419d6b0197424e576bd1b1cbee5c854c3 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 25 Nov 2021 01:05:32 +0000 Subject: [PATCH 104/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 1a8056f1ed..2624760b04 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-11-04_01:03:41 +# VULN_SCAN_TIME=2021-11-25_01:05:29 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From a3b076f8517b8c2b9007e864ff3bd9b529220487 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 25 Nov 2021 01:05:47 +0000 Subject: [PATCH 105/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 8466c5b4aa..9862cbeae0 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-11-10_01:04:25 +# VULN_SCAN_TIME=2021-11-25_01:05:45 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From ff87344a2923e42a4ea47cc7c44d015de13733a6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 26 Nov 2021 17:18:24 +0100 Subject: [PATCH 106/898] bump jupyterhub to 2.0.0rc5 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 0a72c756a6..25fff5aadd 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0rc4 +jupyterhub==2.0.0rc5 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7707f5ed34..c92a02b4e0 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0rc4 +jupyterhub==2.0.0rc5 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 529f7b8d8b..fdc334ef9f 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0rc4 +jupyterhub==2.0.0rc5 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 92c9a19445..4df4cbb0cf 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0rc4 +appVersion: 2.0.0rc5 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 4206f56d285b6048f2ae2ed18df9b5071e7deb4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 00:20:45 +0000 Subject: [PATCH 107/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 1.1.2 to 2.0.0. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/1.1.2...2.0.0) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index c92a02b4e0..d02a6ee895 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -77,7 +77,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==1.1.2 +jupyterhub-kubespawner==2.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From a6ccc909a78efb745d61f6f8d8da65783e4a95da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 05:06:03 +0000 Subject: [PATCH 108/898] build(deps): bump aquasecurity/trivy-action from 0.1.0 to 0.2.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.1.0 to 0.2.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/2a2157eb22c08c9a1fac99263430307b8d1bc7a2...0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 21b0500fa8..8696ad2d5f 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -82,7 +82,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 + uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -105,7 +105,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 + uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -164,7 +164,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@2a2157eb22c08c9a1fac99263430307b8d1bc7a2 + uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 with: image-ref: rebuilt-image format: table From f3aa8df4f8631e91dba8683ad6ca307f779aa103 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Nov 2021 20:06:14 +0000 Subject: [PATCH 109/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v2.4.1 → v2.5.0](https://github.com/pre-commit/mirrors-prettier/compare/v2.4.1...v2.5.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e3dd0afe81..eeb163745b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.4.1 + rev: v2.5.0 hooks: - id: prettier From 7915aadf7f5c704491408d795cdd44f6ef986f05 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 1 Dec 2021 15:10:07 +0100 Subject: [PATCH 110/898] bump jupyterhub to 2.0.0 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 25fff5aadd..8c3013aad9 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0rc5 +jupyterhub==2.0.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index d02a6ee895..82c7fca350 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0rc5 +jupyterhub==2.0.0 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index fdc334ef9f..022a984004 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0rc5 +jupyterhub==2.0.0 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 4df4cbb0cf..ecea77f00b 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0rc5 +appVersion: 2.0.0 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 1f75fa31f66b69a2a6cdc102554cf7cbe8df4889 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Dec 2021 21:08:03 +0000 Subject: [PATCH 111/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.11b1 → 21.12b0](https://github.com/psf/black/compare/21.11b1...21.12b0) - [github.com/pre-commit/mirrors-prettier: v2.5.0 → v2.5.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.5.0...v2.5.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eeb163745b..67aa19883c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 21.11b1 + rev: 21.12b0 hooks: - id: black args: [--target-version=py36] @@ -42,7 +42,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.5.0 + rev: v2.5.1 hooks: - id: prettier From 4af69d8f5444a6f2403a47874319318c5388d633 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Dec 2021 00:16:59 +0000 Subject: [PATCH 112/898] build(deps): bump sqlalchemy-cockroachdb in /images/hub Bumps [sqlalchemy-cockroachdb](https://github.com/cockroachdb/sqlalchemy-cockroachdb) from 1.4.2 to 1.4.3. - [Release notes](https://github.com/cockroachdb/sqlalchemy-cockroachdb/releases) - [Changelog](https://github.com/cockroachdb/sqlalchemy-cockroachdb/blob/master/CHANGES.md) - [Commits](https://github.com/cockroachdb/sqlalchemy-cockroachdb/compare/v1.4.2...v1.4.3) --- updated-dependencies: - dependency-name: sqlalchemy-cockroachdb dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 82c7fca350..398a1725a6 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -202,7 +202,7 @@ sqlalchemy==1.4.23 # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.2 +sqlalchemy-cockroachdb==1.4.3 # via -r requirements.in statsd==3.3.0 # via -r requirements.in From 20d48579a6531ad69bcfab8b0c9d3653bd1ba29e Mon Sep 17 00:00:00 2001 From: "Wileam Y. Phan" <50928756+wyphan@users.noreply.github.com> Date: Mon, 13 Dec 2021 11:45:49 -0500 Subject: [PATCH 113/898] Update MetalLB section in step-zero-microk8s.md The `microk8s enable metallb` command previously listed there gives me the following error: ``` Your input value (10.0.0.100-200) is not a valid IP Range ``` --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md index 571088db47..6949944888 100644 --- a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -35,7 +35,7 @@ This guide describes how to configure MicroK8s to work with Zero to Juptyerhub f MetalLB has two modes: [Layer 2 Mode](https://metallb.universe.tf/concepts/layer2/), the default and recommended mode, and [BGP Mode](https://metallb.universe.tf/concepts/bgp/). In Layer 2 mode, MetalLB needs a range of IP addresses that are on the same network as the host running MicroK8s. If the host has multiple interfaces you can choose addresses from of _any_ of the interfaces. The range you give MetalLB can have as few as one IP address. When a `LoadBalancer` resource is requested MetalLB automatically adds one of its IP addresses to the interface of your host and passes the traffic into your Kubernetes system. This example shows how to enable a pool of addresses from `10.0.0.100` to `10.0.0.200`: ``` - microk8s enable metallb:10.0.0.100-200 + microk8s enable metallb:10.0.0.100-10.0.0.200 ``` If you give MetalLB a range of IP addresses you can choose one in your JupyterHub configuration. This is particularly important if you need TLS because you will have to setup a DNS entry for your server that has a fixed IP address. Here's an example proxy configuration with a fixed IP address request: From 6b246ca4e098f202cf271d33615f77e3f705065a Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 14 Dec 2021 01:10:08 +0000 Subject: [PATCH 114/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 2624760b04..c577ad38c5 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-11-25_01:05:29 +# VULN_SCAN_TIME=2021-12-14_01:10:06 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 8e218e586de422e2dd8b3ea6ed8da135a7cc991a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Dec 2021 05:06:01 +0000 Subject: [PATCH 115/898] build(deps): bump peter-evans/create-pull-request from 3.11.0 to 3.12.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.11.0 to 3.12.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/67df31e08a133c6a77008b89689677067fef169e...dcd5fd746d53dd8de555c0f10bca6c35628be47a) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 8696ad2d5f..24f3b47b10 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -198,7 +198,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@67df31e08a133c6a77008b89689677067fef169e + uses: peter-evans/create-pull-request@dcd5fd746d53dd8de555c0f10bca6c35628be47a with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From c75691d8a0cf683afbebcf321894b235e6ad4367 Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Tue, 14 Dec 2021 21:19:53 -0500 Subject: [PATCH 116/898] DOC: instructions for role creation in AWS --- doc/source/kubernetes/amazon/step-zero-aws.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/doc/source/kubernetes/amazon/step-zero-aws.md index 085da10771..75ede5977e 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws.md +++ b/doc/source/kubernetes/amazon/step-zero-aws.md @@ -14,7 +14,9 @@ template you will use to setup and shape your cluster. 1. Create an IAM Role This role will be used to give your CI host permission to create and destroy - resources on AWS + resources on AWS. Instructions for creating a role can be found + [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create.html). + The following policies are required: - AmazonEC2FullAccess - IAMFullAccess From 49b057e0656cf97e666f102eca1e7b89f38d3c84 Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Tue, 14 Dec 2021 21:25:55 -0500 Subject: [PATCH 117/898] DOC: role creation instructions --- doc/source/kubernetes/amazon/step-zero-aws-eks.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws-eks.md b/doc/source/kubernetes/amazon/step-zero-aws-eks.md index 05dbd3c71b..de2ef74257 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws-eks.md +++ b/doc/source/kubernetes/amazon/step-zero-aws-eks.md @@ -9,7 +9,9 @@ This guide uses AWS to set up a cluster. This mirrors the steps found at [Gettin ## Procedure 1. Create a IAM Role for EKS Service Role. - It should have the following policies + Instructions for creating a role can be found + [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create.html). + It should have the following policies: - AmazonEKSClusterPolicy - AmazonEKSServicePolicy From b84bfc6240294bc70b900cab0b24db586bca13af Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 15 Dec 2021 13:37:41 +0530 Subject: [PATCH 118/898] Document how to disable some labextensions with config Porting this blog post into the docs: https://words.yuvi.in/post/jupyterlab-settings-z2jh/ --- .../customizing/user-environment.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 1a43db3492..2fc7845d5d 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -541,3 +541,43 @@ Prior to 2.0, the default behavior of zero-to-jupyterhub was to launch `jupyterh ignoring what was in the image. The default command is now whatever the image runs by default. ``` + +## Disable specific JupyterLab extensions + +Sometimes you want to temporarily disable a JupyterLab extension on a JupyterHub +by default, without having to rebuild your docker image. This can be very +easily done with [`singleuser.extraFiles`](schema_singleuser.extraFiles). +and JupyterLab's [page_config.json](https://jupyterlab.readthedocs.io/en/stable/user/directories.html#labconfig-directories) + +JupyterLab's `page_config.json` lets you set page configuration by dropping JSON files +under a `labconfig` directory inside any of the directories listed when you run `jupyter --paths`. +We just use `singleuser.extraFiles` to provide this file! + +```yaml +singleuser: + extraFiles: + lab-config: + mountPath: /etc/jupyter/labconfig/page_config.json + data: + disabledExtensions: + jupyterlab-link-share: true +``` + +This will disable the [link-share](https://github.com/jupyterlab-contrib/jupyterlab-link-share) +labextension, both in JupyterLab and RetroLab. You can find the name of the +extension, as well as its current status, with `jupyter labextension list`. + +``` +jovyan@jupyter-yuvipanda:~$ jupyter labextension list +JupyterLab v3.2.4 +/opt/conda/share/jupyter/labextensions + jupyterlab-plotly v5.4.0 enabled OK + jupyter-matplotlib v0.9.0 enabled OK + jupyterlab-link-share v0.2.4 disabled OK (python, jupyterlab-link-share) + @jupyter-widgets/jupyterlab-manager v3.0.1 enabled OK (python, jupyterlab_widgets) + @jupyter-server/resource-usage v0.6.0 enabled OK (python, jupyter-resource-usage) + @retrolab/lab-extension v0.3.13 enabled OK +``` + +This is extremely helpful if the same image is being shared across hubs, and +you want some of the hubs to have some of the extensions disabled. From 22f8034f74fb623aeb6b480eafe55cdc8f153d59 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 15 Dec 2021 14:02:14 +0530 Subject: [PATCH 119/898] Fix indent --- doc/source/jupyterhub/customizing/user-environment.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index 2fc7845d5d..b1410bb37b 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -556,11 +556,11 @@ We just use `singleuser.extraFiles` to provide this file! ```yaml singleuser: extraFiles: - lab-config: - mountPath: /etc/jupyter/labconfig/page_config.json - data: - disabledExtensions: - jupyterlab-link-share: true + lab-config: + mountPath: /etc/jupyter/labconfig/page_config.json + data: + disabledExtensions: + jupyterlab-link-share: true ``` This will disable the [link-share](https://github.com/jupyterlab-contrib/jupyterlab-link-share) From 722e5b4bae0bd26840b72eb27989a173c3069413 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 15 Dec 2021 14:16:49 +0000 Subject: [PATCH 120/898] Improve documentation for singleuser.fsGid --- jupyterhub/schema.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ab1562cac3..76edfeb54b 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2020,7 +2020,11 @@ properties: description: *kubespawner-native-config-description fsGid: type: [integer, "null"] - description: *kubespawner-native-config-description + description: | + The GID of the group that should own any volumes that are created and mounted. + You’ll have to set this if you are using auto-provisioned volumes with most cloud providers. + See `fs_gid` in the [KubeSpawner + documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html). lifecycleHooks: type: object additionalProperties: false From 760a5c8cd3f275d545c15274656189ad81dd085b Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Thu, 16 Dec 2021 00:12:52 -0500 Subject: [PATCH 121/898] DOC: add extra AWS ssh info --- doc/source/kubernetes/amazon/step-zero-aws.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/doc/source/kubernetes/amazon/step-zero-aws.md index 75ede5977e..ba5d47cf68 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws.md +++ b/doc/source/kubernetes/amazon/step-zero-aws.md @@ -31,9 +31,18 @@ template you will use to setup and shape your cluster. When creating it, assign the IAM role created in step 1. - Once created, download ssh keys. + Once created, download ssh keys (.pem file). Ensure permissions are restrictive on the file: + + ``` + chmod 400 name.pem + ``` + +3. SSH to your CI host as: + + ``` + ssh -i /path/name.pem ec2-user@my-instance-Private-IPv4-address + ``` -3. SSH to your CI host 4. Install kops and kubectl on your CI host - Follow the instructions here: From fa5a9b0fa7662c91ce4af8a84618f3c0522564b4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 16 Dec 2021 05:13:51 +0000 Subject: [PATCH 122/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- doc/source/kubernetes/amazon/step-zero-aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/doc/source/kubernetes/amazon/step-zero-aws.md index ba5d47cf68..552bf30d03 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws.md +++ b/doc/source/kubernetes/amazon/step-zero-aws.md @@ -32,7 +32,7 @@ template you will use to setup and shape your cluster. When creating it, assign the IAM role created in step 1. Once created, download ssh keys (.pem file). Ensure permissions are restrictive on the file: - + ``` chmod 400 name.pem ``` From b201cdec5fc3435c7f458f5f903a942ca544ff61 Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Thu, 16 Dec 2021 10:16:46 -0500 Subject: [PATCH 123/898] DOC: ssh instructions --- doc/source/kubernetes/amazon/step-zero-aws.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/doc/source/kubernetes/amazon/step-zero-aws.md index 552bf30d03..96fc0ef226 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws.md +++ b/doc/source/kubernetes/amazon/step-zero-aws.md @@ -37,11 +37,8 @@ template you will use to setup and shape your cluster. chmod 400 name.pem ``` -3. SSH to your CI host as: - - ``` - ssh -i /path/name.pem ec2-user@my-instance-Private-IPv4-address - ``` +3. SSH to your CI host. Instructions on how to do this are given + [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html) 4. Install kops and kubectl on your CI host From 3236c3e0aeb38957649a20fca83f8bbe790e1f6e Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Thu, 16 Dec 2021 11:35:20 -0500 Subject: [PATCH 124/898] DOC: full stop --- doc/source/kubernetes/amazon/step-zero-aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/doc/source/kubernetes/amazon/step-zero-aws.md index 96fc0ef226..715da7dd75 100644 --- a/doc/source/kubernetes/amazon/step-zero-aws.md +++ b/doc/source/kubernetes/amazon/step-zero-aws.md @@ -38,7 +38,7 @@ template you will use to setup and shape your cluster. ``` 3. SSH to your CI host. Instructions on how to do this are given - [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html) + [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html). 4. Install kops and kubectl on your CI host From 27e601987583a608d55f06e1731eae504ab18571 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Thu, 16 Dec 2021 21:49:49 +0000 Subject: [PATCH 125/898] Link to Discourse instead of the mailing list --- doc/source/administrator/upgrading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/administrator/upgrading.md b/doc/source/administrator/upgrading.md index bd3001c950..d29be05054 100644 --- a/doc/source/administrator/upgrading.md +++ b/doc/source/administrator/upgrading.md @@ -9,7 +9,7 @@ changes to your deployment. Check the [CHANGELOG](https://github.com/jupyterhub/ for each release to find out if there are any breaking changes in the newest version. For additional help, feel free to reach out to us on [gitter](https://gitter.im/jupyterhub/jupyterhub) -or the [mailing list](https://groups.google.com/forum/#!forum/jupyter)! +or the [Discourse forum](https://discourse.jupyter.org/). ## Major helm-chart upgrades From 30b336e4ad1a5eaa7000ce86c603a702293b7b0f Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 21 Dec 2021 20:59:55 +0000 Subject: [PATCH 126/898] Don't pin example jupyter/minimal-notebook People are going to copy and paste, so use `latest` instead of a fixed tag in the example --- doc/source/jupyterhub/customizing/user-environment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/doc/source/jupyterhub/customizing/user-environment.md index b1410bb37b..0fea082bd5 100644 --- a/doc/source/jupyterhub/customizing/user-environment.md +++ b/doc/source/jupyterhub/customizing/user-environment.md @@ -234,8 +234,8 @@ image. This file can be built to a {term}`Docker image`, and pushed to a by the Helm chart. ```Dockerfile -FROM jupyter/minimal-notebook:177037d09156 -# Get the latest image tag at: +FROM jupyter/minimal-notebook:latest +# Replace `latest` with an image tag from to ensure reproducible builds: # https://hub.docker.com/r/jupyter/minimal-notebook/tags/ # Inspect the Dockerfile at: # https://github.com/jupyter/docker-stacks/tree/HEAD/minimal-notebook/Dockerfile From ba5b8d0049032574a0fd325fcdb5a0f70db500b1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 15:01:27 +0100 Subject: [PATCH 127/898] Rename doc folder to docs --- {doc => docs}/Makefile | 0 {doc => docs}/README.md | 0 {doc => docs}/make.bat | 0 {doc => docs}/requirements.txt | 0 {doc => docs}/source/_static/custom.css | 0 .../source/_static/images/architecture.png | Bin .../source/_static/images/azure/cli_start.png | Bin .../source/_static/images/azure/scale_condition.png | Bin .../source/_static/images/azure/scale_in.png | Bin .../source/_static/images/azure/scale_out.png | Bin .../source/_static/images/azure/select_vmss.png | Bin .../source/_static/images/data8_audience.jpg | Bin .../_static/images/data8_massive_audience.jpg | Bin .../_static/images/google/start_interactive_cli.png | Bin .../create-free-kubernetes-cluster-ibm-cloud.png | Bin .../create-paid-kubernetes-cluster-ibm-cloud.png | Bin .../_static/images/ibm/kubectl-cluster-info.png | Bin .../source/_static/images/logo/favicon.ico | Bin {doc => docs}/source/_static/images/logo/logo.png | Bin .../images/no-longer-referenced/authenticate.png | Bin .../no-longer-referenced/authenticate_success.png | Bin .../no-longer-referenced/cloud_sdk_doc_landing.png | Bin .../no-longer-referenced/cloud_sdk_landing.png | Bin .../container_engine_location.jpg | Bin .../no-longer-referenced/gcloud_cluster_created.png | Bin .../gcloud_container_clusters_create.png | Bin .../images/no-longer-referenced/gcloud_init.png | Bin .../images/no-longer-referenced/install_sdk1.png | Bin .../no-longer-referenced/ovh-project-name.png | Bin .../service_account_compute_engine.png | Bin .../source/_static/images/ovh/add-nodes.png | Bin .../_static/images/ovh/create-cluster-button.png | Bin .../_static/images/ovh/create-cluster-options.png | Bin .../source/_static/images/ovh/create-ovh-stack.png | Bin .../source/_static/images/ovh/kubeconfig.png | Bin {doc => docs}/source/_static/images/ovh/payment.png | Bin .../source/_static/images/ovh/public-cloud.png | Bin .../source/_static/images/user_scheduler.png | Bin {doc => docs}/source/administrator/advanced.md | 0 {doc => docs}/source/administrator/architecture.md | 0 .../source/administrator/authentication.md | 0 {doc => docs}/source/administrator/cost.md | 0 {doc => docs}/source/administrator/debug.md | 0 {doc => docs}/source/administrator/index.md | 0 {doc => docs}/source/administrator/optimization.md | 0 {doc => docs}/source/administrator/security.md | 0 {doc => docs}/source/administrator/services.md | 0 .../source/administrator/troubleshooting.md | 0 {doc => docs}/source/administrator/upgrading.md | 0 {doc => docs}/source/conf.py | 0 {doc => docs}/source/index.md | 0 {doc => docs}/source/jupyterhub/customization.md | 0 .../jupyterhub/customizing/extending-jupyterhub.md | 0 .../jupyterhub/customizing/user-environment.md | 0 .../jupyterhub/customizing/user-management.md | 0 .../source/jupyterhub/customizing/user-resources.md | 0 .../source/jupyterhub/customizing/user-storage.md | 0 {doc => docs}/source/jupyterhub/index.md | 0 {doc => docs}/source/jupyterhub/installation.md | 0 {doc => docs}/source/jupyterhub/uninstall.md | 0 .../source/kubernetes/amazon/efs_storage.md | 0 .../source/kubernetes/amazon/step-zero-aws-eks.md | 0 .../source/kubernetes/amazon/step-zero-aws.md | 0 .../digital-ocean/step-zero-digital-ocean.md | 0 .../source/kubernetes/google/step-zero-gcp.md | 0 .../source/kubernetes/ibm/step-zero-ibm.md | 0 {doc => docs}/source/kubernetes/index.md | 0 .../microsoft/step-zero-azure-autoscale.md | 0 .../source/kubernetes/microsoft/step-zero-azure.md | 0 {doc => docs}/source/kubernetes/openstack/.gitkeep | 0 .../source/kubernetes/other-infrastructure/.gitkeep | 0 .../other-infrastructure/step-zero-microk8s.md | 0 .../source/kubernetes/ovh/step-zero-ovh.md | 0 .../source/kubernetes/redhat/step-zero-openshift.md | 0 {doc => docs}/source/kubernetes/setup-helm.md | 0 {doc => docs}/source/kubernetes/setup-kubernetes.md | 0 {doc => docs}/source/repo2docker.md | 0 {doc => docs}/source/resources/community.md | 0 {doc => docs}/source/resources/glossary.md | 0 {doc => docs}/source/resources/index.md | 0 {doc => docs}/source/resources/reference-docs.md | 0 {doc => docs}/source/resources/reference.txt | 0 {doc => docs}/source/resources/tools.md | 0 83 files changed, 0 insertions(+), 0 deletions(-) rename {doc => docs}/Makefile (100%) rename {doc => docs}/README.md (100%) rename {doc => docs}/make.bat (100%) rename {doc => docs}/requirements.txt (100%) rename {doc => docs}/source/_static/custom.css (100%) rename {doc => docs}/source/_static/images/architecture.png (100%) rename {doc => docs}/source/_static/images/azure/cli_start.png (100%) rename {doc => docs}/source/_static/images/azure/scale_condition.png (100%) rename {doc => docs}/source/_static/images/azure/scale_in.png (100%) rename {doc => docs}/source/_static/images/azure/scale_out.png (100%) rename {doc => docs}/source/_static/images/azure/select_vmss.png (100%) rename {doc => docs}/source/_static/images/data8_audience.jpg (100%) rename {doc => docs}/source/_static/images/data8_massive_audience.jpg (100%) rename {doc => docs}/source/_static/images/google/start_interactive_cli.png (100%) rename {doc => docs}/source/_static/images/ibm/create-free-kubernetes-cluster-ibm-cloud.png (100%) rename {doc => docs}/source/_static/images/ibm/create-paid-kubernetes-cluster-ibm-cloud.png (100%) rename {doc => docs}/source/_static/images/ibm/kubectl-cluster-info.png (100%) rename {doc => docs}/source/_static/images/logo/favicon.ico (100%) rename {doc => docs}/source/_static/images/logo/logo.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/authenticate.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/authenticate_success.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/cloud_sdk_doc_landing.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/cloud_sdk_landing.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/container_engine_location.jpg (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/gcloud_cluster_created.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/gcloud_container_clusters_create.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/gcloud_init.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/install_sdk1.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/ovh-project-name.png (100%) rename {doc => docs}/source/_static/images/no-longer-referenced/service_account_compute_engine.png (100%) rename {doc => docs}/source/_static/images/ovh/add-nodes.png (100%) rename {doc => docs}/source/_static/images/ovh/create-cluster-button.png (100%) rename {doc => docs}/source/_static/images/ovh/create-cluster-options.png (100%) rename {doc => docs}/source/_static/images/ovh/create-ovh-stack.png (100%) rename {doc => docs}/source/_static/images/ovh/kubeconfig.png (100%) rename {doc => docs}/source/_static/images/ovh/payment.png (100%) rename {doc => docs}/source/_static/images/ovh/public-cloud.png (100%) rename {doc => docs}/source/_static/images/user_scheduler.png (100%) rename {doc => docs}/source/administrator/advanced.md (100%) rename {doc => docs}/source/administrator/architecture.md (100%) rename {doc => docs}/source/administrator/authentication.md (100%) rename {doc => docs}/source/administrator/cost.md (100%) rename {doc => docs}/source/administrator/debug.md (100%) rename {doc => docs}/source/administrator/index.md (100%) rename {doc => docs}/source/administrator/optimization.md (100%) rename {doc => docs}/source/administrator/security.md (100%) rename {doc => docs}/source/administrator/services.md (100%) rename {doc => docs}/source/administrator/troubleshooting.md (100%) rename {doc => docs}/source/administrator/upgrading.md (100%) rename {doc => docs}/source/conf.py (100%) rename {doc => docs}/source/index.md (100%) rename {doc => docs}/source/jupyterhub/customization.md (100%) rename {doc => docs}/source/jupyterhub/customizing/extending-jupyterhub.md (100%) rename {doc => docs}/source/jupyterhub/customizing/user-environment.md (100%) rename {doc => docs}/source/jupyterhub/customizing/user-management.md (100%) rename {doc => docs}/source/jupyterhub/customizing/user-resources.md (100%) rename {doc => docs}/source/jupyterhub/customizing/user-storage.md (100%) rename {doc => docs}/source/jupyterhub/index.md (100%) rename {doc => docs}/source/jupyterhub/installation.md (100%) rename {doc => docs}/source/jupyterhub/uninstall.md (100%) rename {doc => docs}/source/kubernetes/amazon/efs_storage.md (100%) rename {doc => docs}/source/kubernetes/amazon/step-zero-aws-eks.md (100%) rename {doc => docs}/source/kubernetes/amazon/step-zero-aws.md (100%) rename {doc => docs}/source/kubernetes/digital-ocean/step-zero-digital-ocean.md (100%) rename {doc => docs}/source/kubernetes/google/step-zero-gcp.md (100%) rename {doc => docs}/source/kubernetes/ibm/step-zero-ibm.md (100%) rename {doc => docs}/source/kubernetes/index.md (100%) rename {doc => docs}/source/kubernetes/microsoft/step-zero-azure-autoscale.md (100%) rename {doc => docs}/source/kubernetes/microsoft/step-zero-azure.md (100%) rename {doc => docs}/source/kubernetes/openstack/.gitkeep (100%) rename {doc => docs}/source/kubernetes/other-infrastructure/.gitkeep (100%) rename {doc => docs}/source/kubernetes/other-infrastructure/step-zero-microk8s.md (100%) rename {doc => docs}/source/kubernetes/ovh/step-zero-ovh.md (100%) rename {doc => docs}/source/kubernetes/redhat/step-zero-openshift.md (100%) rename {doc => docs}/source/kubernetes/setup-helm.md (100%) rename {doc => docs}/source/kubernetes/setup-kubernetes.md (100%) rename {doc => docs}/source/repo2docker.md (100%) rename {doc => docs}/source/resources/community.md (100%) rename {doc => docs}/source/resources/glossary.md (100%) rename {doc => docs}/source/resources/index.md (100%) rename {doc => docs}/source/resources/reference-docs.md (100%) rename {doc => docs}/source/resources/reference.txt (100%) rename {doc => docs}/source/resources/tools.md (100%) diff --git a/doc/Makefile b/docs/Makefile similarity index 100% rename from doc/Makefile rename to docs/Makefile diff --git a/doc/README.md b/docs/README.md similarity index 100% rename from doc/README.md rename to docs/README.md diff --git a/doc/make.bat b/docs/make.bat similarity index 100% rename from doc/make.bat rename to docs/make.bat diff --git a/doc/requirements.txt b/docs/requirements.txt similarity index 100% rename from doc/requirements.txt rename to docs/requirements.txt diff --git a/doc/source/_static/custom.css b/docs/source/_static/custom.css similarity index 100% rename from doc/source/_static/custom.css rename to docs/source/_static/custom.css diff --git a/doc/source/_static/images/architecture.png b/docs/source/_static/images/architecture.png similarity index 100% rename from doc/source/_static/images/architecture.png rename to docs/source/_static/images/architecture.png diff --git a/doc/source/_static/images/azure/cli_start.png b/docs/source/_static/images/azure/cli_start.png similarity index 100% rename from doc/source/_static/images/azure/cli_start.png rename to docs/source/_static/images/azure/cli_start.png diff --git a/doc/source/_static/images/azure/scale_condition.png b/docs/source/_static/images/azure/scale_condition.png similarity index 100% rename from doc/source/_static/images/azure/scale_condition.png rename to docs/source/_static/images/azure/scale_condition.png diff --git a/doc/source/_static/images/azure/scale_in.png b/docs/source/_static/images/azure/scale_in.png similarity index 100% rename from doc/source/_static/images/azure/scale_in.png rename to docs/source/_static/images/azure/scale_in.png diff --git a/doc/source/_static/images/azure/scale_out.png b/docs/source/_static/images/azure/scale_out.png similarity index 100% rename from doc/source/_static/images/azure/scale_out.png rename to docs/source/_static/images/azure/scale_out.png diff --git a/doc/source/_static/images/azure/select_vmss.png b/docs/source/_static/images/azure/select_vmss.png similarity index 100% rename from doc/source/_static/images/azure/select_vmss.png rename to docs/source/_static/images/azure/select_vmss.png diff --git a/doc/source/_static/images/data8_audience.jpg b/docs/source/_static/images/data8_audience.jpg similarity index 100% rename from doc/source/_static/images/data8_audience.jpg rename to docs/source/_static/images/data8_audience.jpg diff --git a/doc/source/_static/images/data8_massive_audience.jpg b/docs/source/_static/images/data8_massive_audience.jpg similarity index 100% rename from doc/source/_static/images/data8_massive_audience.jpg rename to docs/source/_static/images/data8_massive_audience.jpg diff --git a/doc/source/_static/images/google/start_interactive_cli.png b/docs/source/_static/images/google/start_interactive_cli.png similarity index 100% rename from doc/source/_static/images/google/start_interactive_cli.png rename to docs/source/_static/images/google/start_interactive_cli.png diff --git a/doc/source/_static/images/ibm/create-free-kubernetes-cluster-ibm-cloud.png b/docs/source/_static/images/ibm/create-free-kubernetes-cluster-ibm-cloud.png similarity index 100% rename from doc/source/_static/images/ibm/create-free-kubernetes-cluster-ibm-cloud.png rename to docs/source/_static/images/ibm/create-free-kubernetes-cluster-ibm-cloud.png diff --git a/doc/source/_static/images/ibm/create-paid-kubernetes-cluster-ibm-cloud.png b/docs/source/_static/images/ibm/create-paid-kubernetes-cluster-ibm-cloud.png similarity index 100% rename from doc/source/_static/images/ibm/create-paid-kubernetes-cluster-ibm-cloud.png rename to docs/source/_static/images/ibm/create-paid-kubernetes-cluster-ibm-cloud.png diff --git a/doc/source/_static/images/ibm/kubectl-cluster-info.png b/docs/source/_static/images/ibm/kubectl-cluster-info.png similarity index 100% rename from doc/source/_static/images/ibm/kubectl-cluster-info.png rename to docs/source/_static/images/ibm/kubectl-cluster-info.png diff --git a/doc/source/_static/images/logo/favicon.ico b/docs/source/_static/images/logo/favicon.ico similarity index 100% rename from doc/source/_static/images/logo/favicon.ico rename to docs/source/_static/images/logo/favicon.ico diff --git a/doc/source/_static/images/logo/logo.png b/docs/source/_static/images/logo/logo.png similarity index 100% rename from doc/source/_static/images/logo/logo.png rename to docs/source/_static/images/logo/logo.png diff --git a/doc/source/_static/images/no-longer-referenced/authenticate.png b/docs/source/_static/images/no-longer-referenced/authenticate.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/authenticate.png rename to docs/source/_static/images/no-longer-referenced/authenticate.png diff --git a/doc/source/_static/images/no-longer-referenced/authenticate_success.png b/docs/source/_static/images/no-longer-referenced/authenticate_success.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/authenticate_success.png rename to docs/source/_static/images/no-longer-referenced/authenticate_success.png diff --git a/doc/source/_static/images/no-longer-referenced/cloud_sdk_doc_landing.png b/docs/source/_static/images/no-longer-referenced/cloud_sdk_doc_landing.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/cloud_sdk_doc_landing.png rename to docs/source/_static/images/no-longer-referenced/cloud_sdk_doc_landing.png diff --git a/doc/source/_static/images/no-longer-referenced/cloud_sdk_landing.png b/docs/source/_static/images/no-longer-referenced/cloud_sdk_landing.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/cloud_sdk_landing.png rename to docs/source/_static/images/no-longer-referenced/cloud_sdk_landing.png diff --git a/doc/source/_static/images/no-longer-referenced/container_engine_location.jpg b/docs/source/_static/images/no-longer-referenced/container_engine_location.jpg similarity index 100% rename from doc/source/_static/images/no-longer-referenced/container_engine_location.jpg rename to docs/source/_static/images/no-longer-referenced/container_engine_location.jpg diff --git a/doc/source/_static/images/no-longer-referenced/gcloud_cluster_created.png b/docs/source/_static/images/no-longer-referenced/gcloud_cluster_created.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/gcloud_cluster_created.png rename to docs/source/_static/images/no-longer-referenced/gcloud_cluster_created.png diff --git a/doc/source/_static/images/no-longer-referenced/gcloud_container_clusters_create.png b/docs/source/_static/images/no-longer-referenced/gcloud_container_clusters_create.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/gcloud_container_clusters_create.png rename to docs/source/_static/images/no-longer-referenced/gcloud_container_clusters_create.png diff --git a/doc/source/_static/images/no-longer-referenced/gcloud_init.png b/docs/source/_static/images/no-longer-referenced/gcloud_init.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/gcloud_init.png rename to docs/source/_static/images/no-longer-referenced/gcloud_init.png diff --git a/doc/source/_static/images/no-longer-referenced/install_sdk1.png b/docs/source/_static/images/no-longer-referenced/install_sdk1.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/install_sdk1.png rename to docs/source/_static/images/no-longer-referenced/install_sdk1.png diff --git a/doc/source/_static/images/no-longer-referenced/ovh-project-name.png b/docs/source/_static/images/no-longer-referenced/ovh-project-name.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/ovh-project-name.png rename to docs/source/_static/images/no-longer-referenced/ovh-project-name.png diff --git a/doc/source/_static/images/no-longer-referenced/service_account_compute_engine.png b/docs/source/_static/images/no-longer-referenced/service_account_compute_engine.png similarity index 100% rename from doc/source/_static/images/no-longer-referenced/service_account_compute_engine.png rename to docs/source/_static/images/no-longer-referenced/service_account_compute_engine.png diff --git a/doc/source/_static/images/ovh/add-nodes.png b/docs/source/_static/images/ovh/add-nodes.png similarity index 100% rename from doc/source/_static/images/ovh/add-nodes.png rename to docs/source/_static/images/ovh/add-nodes.png diff --git a/doc/source/_static/images/ovh/create-cluster-button.png b/docs/source/_static/images/ovh/create-cluster-button.png similarity index 100% rename from doc/source/_static/images/ovh/create-cluster-button.png rename to docs/source/_static/images/ovh/create-cluster-button.png diff --git a/doc/source/_static/images/ovh/create-cluster-options.png b/docs/source/_static/images/ovh/create-cluster-options.png similarity index 100% rename from doc/source/_static/images/ovh/create-cluster-options.png rename to docs/source/_static/images/ovh/create-cluster-options.png diff --git a/doc/source/_static/images/ovh/create-ovh-stack.png b/docs/source/_static/images/ovh/create-ovh-stack.png similarity index 100% rename from doc/source/_static/images/ovh/create-ovh-stack.png rename to docs/source/_static/images/ovh/create-ovh-stack.png diff --git a/doc/source/_static/images/ovh/kubeconfig.png b/docs/source/_static/images/ovh/kubeconfig.png similarity index 100% rename from doc/source/_static/images/ovh/kubeconfig.png rename to docs/source/_static/images/ovh/kubeconfig.png diff --git a/doc/source/_static/images/ovh/payment.png b/docs/source/_static/images/ovh/payment.png similarity index 100% rename from doc/source/_static/images/ovh/payment.png rename to docs/source/_static/images/ovh/payment.png diff --git a/doc/source/_static/images/ovh/public-cloud.png b/docs/source/_static/images/ovh/public-cloud.png similarity index 100% rename from doc/source/_static/images/ovh/public-cloud.png rename to docs/source/_static/images/ovh/public-cloud.png diff --git a/doc/source/_static/images/user_scheduler.png b/docs/source/_static/images/user_scheduler.png similarity index 100% rename from doc/source/_static/images/user_scheduler.png rename to docs/source/_static/images/user_scheduler.png diff --git a/doc/source/administrator/advanced.md b/docs/source/administrator/advanced.md similarity index 100% rename from doc/source/administrator/advanced.md rename to docs/source/administrator/advanced.md diff --git a/doc/source/administrator/architecture.md b/docs/source/administrator/architecture.md similarity index 100% rename from doc/source/administrator/architecture.md rename to docs/source/administrator/architecture.md diff --git a/doc/source/administrator/authentication.md b/docs/source/administrator/authentication.md similarity index 100% rename from doc/source/administrator/authentication.md rename to docs/source/administrator/authentication.md diff --git a/doc/source/administrator/cost.md b/docs/source/administrator/cost.md similarity index 100% rename from doc/source/administrator/cost.md rename to docs/source/administrator/cost.md diff --git a/doc/source/administrator/debug.md b/docs/source/administrator/debug.md similarity index 100% rename from doc/source/administrator/debug.md rename to docs/source/administrator/debug.md diff --git a/doc/source/administrator/index.md b/docs/source/administrator/index.md similarity index 100% rename from doc/source/administrator/index.md rename to docs/source/administrator/index.md diff --git a/doc/source/administrator/optimization.md b/docs/source/administrator/optimization.md similarity index 100% rename from doc/source/administrator/optimization.md rename to docs/source/administrator/optimization.md diff --git a/doc/source/administrator/security.md b/docs/source/administrator/security.md similarity index 100% rename from doc/source/administrator/security.md rename to docs/source/administrator/security.md diff --git a/doc/source/administrator/services.md b/docs/source/administrator/services.md similarity index 100% rename from doc/source/administrator/services.md rename to docs/source/administrator/services.md diff --git a/doc/source/administrator/troubleshooting.md b/docs/source/administrator/troubleshooting.md similarity index 100% rename from doc/source/administrator/troubleshooting.md rename to docs/source/administrator/troubleshooting.md diff --git a/doc/source/administrator/upgrading.md b/docs/source/administrator/upgrading.md similarity index 100% rename from doc/source/administrator/upgrading.md rename to docs/source/administrator/upgrading.md diff --git a/doc/source/conf.py b/docs/source/conf.py similarity index 100% rename from doc/source/conf.py rename to docs/source/conf.py diff --git a/doc/source/index.md b/docs/source/index.md similarity index 100% rename from doc/source/index.md rename to docs/source/index.md diff --git a/doc/source/jupyterhub/customization.md b/docs/source/jupyterhub/customization.md similarity index 100% rename from doc/source/jupyterhub/customization.md rename to docs/source/jupyterhub/customization.md diff --git a/doc/source/jupyterhub/customizing/extending-jupyterhub.md b/docs/source/jupyterhub/customizing/extending-jupyterhub.md similarity index 100% rename from doc/source/jupyterhub/customizing/extending-jupyterhub.md rename to docs/source/jupyterhub/customizing/extending-jupyterhub.md diff --git a/doc/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md similarity index 100% rename from doc/source/jupyterhub/customizing/user-environment.md rename to docs/source/jupyterhub/customizing/user-environment.md diff --git a/doc/source/jupyterhub/customizing/user-management.md b/docs/source/jupyterhub/customizing/user-management.md similarity index 100% rename from doc/source/jupyterhub/customizing/user-management.md rename to docs/source/jupyterhub/customizing/user-management.md diff --git a/doc/source/jupyterhub/customizing/user-resources.md b/docs/source/jupyterhub/customizing/user-resources.md similarity index 100% rename from doc/source/jupyterhub/customizing/user-resources.md rename to docs/source/jupyterhub/customizing/user-resources.md diff --git a/doc/source/jupyterhub/customizing/user-storage.md b/docs/source/jupyterhub/customizing/user-storage.md similarity index 100% rename from doc/source/jupyterhub/customizing/user-storage.md rename to docs/source/jupyterhub/customizing/user-storage.md diff --git a/doc/source/jupyterhub/index.md b/docs/source/jupyterhub/index.md similarity index 100% rename from doc/source/jupyterhub/index.md rename to docs/source/jupyterhub/index.md diff --git a/doc/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md similarity index 100% rename from doc/source/jupyterhub/installation.md rename to docs/source/jupyterhub/installation.md diff --git a/doc/source/jupyterhub/uninstall.md b/docs/source/jupyterhub/uninstall.md similarity index 100% rename from doc/source/jupyterhub/uninstall.md rename to docs/source/jupyterhub/uninstall.md diff --git a/doc/source/kubernetes/amazon/efs_storage.md b/docs/source/kubernetes/amazon/efs_storage.md similarity index 100% rename from doc/source/kubernetes/amazon/efs_storage.md rename to docs/source/kubernetes/amazon/efs_storage.md diff --git a/doc/source/kubernetes/amazon/step-zero-aws-eks.md b/docs/source/kubernetes/amazon/step-zero-aws-eks.md similarity index 100% rename from doc/source/kubernetes/amazon/step-zero-aws-eks.md rename to docs/source/kubernetes/amazon/step-zero-aws-eks.md diff --git a/doc/source/kubernetes/amazon/step-zero-aws.md b/docs/source/kubernetes/amazon/step-zero-aws.md similarity index 100% rename from doc/source/kubernetes/amazon/step-zero-aws.md rename to docs/source/kubernetes/amazon/step-zero-aws.md diff --git a/doc/source/kubernetes/digital-ocean/step-zero-digital-ocean.md b/docs/source/kubernetes/digital-ocean/step-zero-digital-ocean.md similarity index 100% rename from doc/source/kubernetes/digital-ocean/step-zero-digital-ocean.md rename to docs/source/kubernetes/digital-ocean/step-zero-digital-ocean.md diff --git a/doc/source/kubernetes/google/step-zero-gcp.md b/docs/source/kubernetes/google/step-zero-gcp.md similarity index 100% rename from doc/source/kubernetes/google/step-zero-gcp.md rename to docs/source/kubernetes/google/step-zero-gcp.md diff --git a/doc/source/kubernetes/ibm/step-zero-ibm.md b/docs/source/kubernetes/ibm/step-zero-ibm.md similarity index 100% rename from doc/source/kubernetes/ibm/step-zero-ibm.md rename to docs/source/kubernetes/ibm/step-zero-ibm.md diff --git a/doc/source/kubernetes/index.md b/docs/source/kubernetes/index.md similarity index 100% rename from doc/source/kubernetes/index.md rename to docs/source/kubernetes/index.md diff --git a/doc/source/kubernetes/microsoft/step-zero-azure-autoscale.md b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md similarity index 100% rename from doc/source/kubernetes/microsoft/step-zero-azure-autoscale.md rename to docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md diff --git a/doc/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md similarity index 100% rename from doc/source/kubernetes/microsoft/step-zero-azure.md rename to docs/source/kubernetes/microsoft/step-zero-azure.md diff --git a/doc/source/kubernetes/openstack/.gitkeep b/docs/source/kubernetes/openstack/.gitkeep similarity index 100% rename from doc/source/kubernetes/openstack/.gitkeep rename to docs/source/kubernetes/openstack/.gitkeep diff --git a/doc/source/kubernetes/other-infrastructure/.gitkeep b/docs/source/kubernetes/other-infrastructure/.gitkeep similarity index 100% rename from doc/source/kubernetes/other-infrastructure/.gitkeep rename to docs/source/kubernetes/other-infrastructure/.gitkeep diff --git a/doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md similarity index 100% rename from doc/source/kubernetes/other-infrastructure/step-zero-microk8s.md rename to docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md diff --git a/doc/source/kubernetes/ovh/step-zero-ovh.md b/docs/source/kubernetes/ovh/step-zero-ovh.md similarity index 100% rename from doc/source/kubernetes/ovh/step-zero-ovh.md rename to docs/source/kubernetes/ovh/step-zero-ovh.md diff --git a/doc/source/kubernetes/redhat/step-zero-openshift.md b/docs/source/kubernetes/redhat/step-zero-openshift.md similarity index 100% rename from doc/source/kubernetes/redhat/step-zero-openshift.md rename to docs/source/kubernetes/redhat/step-zero-openshift.md diff --git a/doc/source/kubernetes/setup-helm.md b/docs/source/kubernetes/setup-helm.md similarity index 100% rename from doc/source/kubernetes/setup-helm.md rename to docs/source/kubernetes/setup-helm.md diff --git a/doc/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md similarity index 100% rename from doc/source/kubernetes/setup-kubernetes.md rename to docs/source/kubernetes/setup-kubernetes.md diff --git a/doc/source/repo2docker.md b/docs/source/repo2docker.md similarity index 100% rename from doc/source/repo2docker.md rename to docs/source/repo2docker.md diff --git a/doc/source/resources/community.md b/docs/source/resources/community.md similarity index 100% rename from doc/source/resources/community.md rename to docs/source/resources/community.md diff --git a/doc/source/resources/glossary.md b/docs/source/resources/glossary.md similarity index 100% rename from doc/source/resources/glossary.md rename to docs/source/resources/glossary.md diff --git a/doc/source/resources/index.md b/docs/source/resources/index.md similarity index 100% rename from doc/source/resources/index.md rename to docs/source/resources/index.md diff --git a/doc/source/resources/reference-docs.md b/docs/source/resources/reference-docs.md similarity index 100% rename from doc/source/resources/reference-docs.md rename to docs/source/resources/reference-docs.md diff --git a/doc/source/resources/reference.txt b/docs/source/resources/reference.txt similarity index 100% rename from doc/source/resources/reference.txt rename to docs/source/resources/reference.txt diff --git a/doc/source/resources/tools.md b/docs/source/resources/tools.md similarity index 100% rename from doc/source/resources/tools.md rename to docs/source/resources/tools.md From d2cca92b19bc655e2fe6a2ecf6ae1d2ce69b8567 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 15:03:53 +0100 Subject: [PATCH 128/898] Adjust to renaming doc/ to docs/ --- .github/workflows/publish.yml | 4 ++-- .github/workflows/test-chart.yaml | 4 ++-- .github/workflows/test-docs.yaml | 10 +++++----- .readthedocs.yml | 4 ++-- CONTRIBUTING.md | 4 ++-- README.md | 2 +- docs/README.md | 2 +- docs/source/conf.py | 2 +- jupyterhub/README.md | 2 +- jupyterhub/schema.yaml | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 057838dc22..02c0c4b3c9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,13 +7,13 @@ name: Publish on: pull_request: paths-ignore: - - "doc/**" + - "docs/**" - "**.md" - ".github/workflows/*" - "!.github/workflows/publish.yaml" push: paths-ignore: - - "doc/**" + - "docs/**" - "**.md" - ".github/workflows/*" - "!.github/workflows/publish.yaml" diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 6700780b13..e91c64daee 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -9,13 +9,13 @@ name: Test chart on: pull_request: paths-ignore: - - "doc/**" + - "docs/**" - "**.md" - ".github/workflows/*" - "!.github/workflows/test-chart.yaml" push: paths-ignore: - - "doc/**" + - "docs/**" - "**.md" - ".github/workflows/*" - "!.github/workflows/test-chart.yaml" diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index f3ea2fea7a..c0cf86ebf2 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -8,12 +8,12 @@ name: Test docs on: pull_request: paths: - - "doc/**" + - "docs/**" - "**/schema.yaml" - "**/test-docs.yaml" push: paths: - - "doc/**" + - "docs/**" - "**/schema.yaml" - "**/test-docs.yaml" branches-ignore: @@ -27,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v2 with: - # chartpress, used by doc/conf.py, requires git history to set + # chartpress, used by docs/conf.py, requires git history to set # chart version and image tags correctly fetch-depth: 0 - uses: actions/setup-python@v2 @@ -35,9 +35,9 @@ jobs: python-version: "3.8" - name: Install deps - run: pip install -r doc/requirements.txt + run: pip install -r docs/requirements.txt - name: make linkcheck run: | - cd doc + cd docs make linkcheck SPHINXOPTS='--color -W --keep-going' diff --git a/.readthedocs.yml b/.readthedocs.yml index 80d78e4d2d..12eca699cc 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,7 +7,7 @@ version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: - configuration: doc/source/conf.py + configuration: docs/source/conf.py # Optionally build your docs in additional formats such as PDF and ePub formats: [] @@ -22,4 +22,4 @@ python: # accepting the existing version. # # ref: https://github.com/readthedocs/readthedocs.org/blob/0e3df509e7810e46603be47d268273c596e68455/readthedocs/doc_builder/python_environments.py#L335-L344 - - requirements: doc/requirements.txt + - requirements: docs/requirements.txt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c76ddb29c6..538c997c80 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ git clone https://github.com/jupyterhub/zero-to-jupyterhub-k8s # Setting up for documentation development -See [doc/README.md](doc/README.md). +See [docs/README.md](docs/README.md). # Setting up for Helm chart development @@ -30,7 +30,7 @@ This needs to be installed: - Python 3.6+ (install at [Anaconda.com](https://www.anaconda.com/distribution/) or [Python.org](https://www.python.org/downloads/)) and dependencies: ```shell pip install -r dev-requirements.txt - pip install -r doc/requirements.txt + pip install -r docs/requirements.txt ``` You can check if you have it all like this: diff --git a/README.md b/README.md index 94a57e4540..56e690a9c0 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Much of the initial groundwork for this documentation is information learned from the successful use of JupyterHub and Kubernetes at UC Berkeley in their [Data 8](http://data8.org/) program. -![](doc/source/_static/images/data8_massive_audience.jpg) +![](docs/source/_static/images/data8_massive_audience.jpg) ## Acknowledgements diff --git a/docs/README.md b/docs/README.md index 3b2647f4ba..dc0d51c4eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,7 @@ in the `readthedocs.yml` file, and made available on ## Local documentation development ```shell -cd doc +cd docs pip install -r requirements.txt ``` diff --git a/docs/source/conf.py b/docs/source/conf.py index 46beb7138a..3d856db012 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -254,7 +254,7 @@ def parse_schema(d, md=[], depth=0, pre=""): "github_user": "jupyterhub", "github_repo": "zero-to-jupyterhub-k8s", "github_version": "main", - "doc_path": "doc/source", + "doc_path": "docs/source", } html_favicon = "_static/images/logo/favicon.ico" diff --git a/jupyterhub/README.md b/jupyterhub/README.md index 9c0527e051..bb85ebb2bd 100644 --- a/jupyterhub/README.md +++ b/jupyterhub/README.md @@ -15,4 +15,4 @@ The JupyterHub Helm chart is accompanied with an installation guide at [z2jh.jup Much of the initial groundwork for this documentation is information learned from the successful use of JupyterHub and Kubernetes at UC Berkeley in their [Data 8](http://data8.org/) program. -![](https://raw.githubusercontent.com/jupyterhub/zero-to-jupyterhub-k8s/HEAD/doc/source/_static/images/data8_massive_audience.jpg) +![](https://raw.githubusercontent.com/jupyterhub/zero-to-jupyterhub-k8s/HEAD/docs/source/_static/images/data8_massive_audience.jpg) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ab1562cac3..414c5da76d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -4,7 +4,7 @@ # # This schema is also used by our documentation system to build the # configuration reference section based on the description fields. See -# doc/source/conf.py for that logic! +# docs/source/conf.py for that logic! # # We look to document everything we have default values for in values.yaml, but # we don't look to enforce the perfect validation logic within this file. From 131439cc38081194c051aece6a34bd0a70c3c77a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 15:04:43 +0100 Subject: [PATCH 129/898] docs: use non-deprecated sphinx config --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3d856db012..6b225382a7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -109,8 +109,8 @@ def _get_git_ref_from_chartpress_based_version(version): # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] -# The master toctree document. -master_doc = "index" +# The root toctree document. +root_doc = master_doc = "index" # The suffix(es) of source filenames. source_suffix = [".md", ".rst"] From 4e01a88c78e8bc34d8ad7f77e38c6de2e7b8ce37 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 15:09:48 +0100 Subject: [PATCH 130/898] docs: use docs/_build like sphinx defaults to --- docs/Makefile | 2 +- docs/make.bat | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Makefile b/docs/Makefile index ac24d8b004..9ef6dac440 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -6,7 +6,7 @@ SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = source -BUILDDIR = build +BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: diff --git a/docs/make.bat b/docs/make.bat index 4f70416558..d367db957b 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -8,7 +8,7 @@ if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=source -set BUILDDIR=build +set BUILDDIR=_build if "%1" == "" goto help if "%1" == "devenv" goto devenv From b88ba30587725ae7079ad731972983708b11410c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 15:02:37 +0100 Subject: [PATCH 131/898] Update .gitignore --- .gitignore | 115 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 79 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index bf073fb275..805555eee8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,17 @@ -### Zero to JupyterHub Kubernetes ### +### Repository specific files that are generated + jupyterhub/values.schema.json +docs/source/resources/reference.md tools/templates/rendered-templates/ -bin/ -.vagrant/ -tools/github.sqlite ci/ephemeral* tmp/ + +### Other misc things + .vscode -### macOS ### +### macOS *.DS_Store .AppleDouble .LSOverride @@ -33,23 +35,17 @@ Network Trash Folder Temporary Items .apdisk -### Vim ### +### Vim + # swap [._]*.s[a-w][a-z] [._]s[a-w][a-z] # session Session.vim -# temporary -.netrwhist -*~ -# auto-generated tag files -tags -# GCloud Credentials -data8-travis-creds.json -#### Python .gitignore from https://github.com/github/gitignore/blob/HEAD/Python.gitignore -#### +### Python .gitignore from https://github.com/github/gitignore/blob/HEAD/Python.gitignore + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -60,9 +56,6 @@ __pycache__/ # Distribution / packaging .Python -env/ -bin/kubectl -bin/minikube build/ develop-eggs/ dist/ @@ -75,9 +68,11 @@ parts/ sdist/ var/ wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST # PyInstaller # Usually these files are written by a python script from a template @@ -92,13 +87,17 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml -*,cover +*.cover +*.py,cover .hypothesis/ +.pytest_cache/ +cover/ # Translations *.mo @@ -107,6 +106,8 @@ coverage.xml # Django stuff: *.log local_settings.py +db.sqlite3 +db.sqlite3-journal # Flask stuff: instance/ @@ -116,42 +117,84 @@ instance/ .scrapy # Sphinx documentation -doc/build/ -doc/source/reference/reference.md -doc/source/resources/reference.md +docs/_build/ # PyBuilder +.pybuilder/ target/ +# Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version +# IPython +profile_default/ +ipython_config.py -# celery beat schedule file +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff celerybeat-schedule +celerybeat.pid -# dotenv -.env +# SageMath parsed files +*.sage.py -# virtualenv +# Environments +.env .venv +env/ venv/ ENV/ -lib64 -pip-selfcheck.json -pyvenv.cfg +env.bak/ +venv.bak/ # Spyder project settings .spyderproject +.spyproject # Rope project settings .ropeproject -# Mac -.DS_STORE +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ -# Emacs -.#* +# Cython debug symbols +cython_debug/ -.idea +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ From 4faa78a924d7fa1c3793a522bc1d68df6bc9c70f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:07:37 +0100 Subject: [PATCH 132/898] deps: bump traefik from 2.4.11 to 2.5.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 408e48da6e..310a9eee59 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -239,7 +239,7 @@ proxy: allowPrivilegeEscalation: false image: name: traefik - tag: v2.4.11 # ref: https://hub.docker.com/_/traefik?tab=tags + tag: v2.5.6 # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 28ca2b37fc38417323b23eb59991f2387410c47f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:08:16 +0100 Subject: [PATCH 133/898] deps: bump kube-scheduler 1.19.13 to 1.19.16 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 310a9eee59..c91fed83be 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -433,7 +433,7 @@ scheduling: # hand with an inspection of the user-scheduelrs RBAC resources # that we have forked. name: k8s.gcr.io/kube-scheduler - tag: v1.19.13 # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: v1.19.16 # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 5a5d2c9791c710a062123b4426b628f0d22fdbef Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:08:39 +0100 Subject: [PATCH 134/898] deps: bump pause image from 3.5 to 3.6 --- jupyterhub/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c91fed83be..c1daf020ab 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -459,7 +459,7 @@ scheduling: # gcloud container images list-tags k8s.gcr.io/pause --sort-by=~tags # # If you update this, also update prePuller.pause.image.tag - tag: "3.5" + tag: "3.6" pullPolicy: pullSecrets: [] replicas: 0 @@ -537,7 +537,7 @@ prePuller: # gcloud container images list-tags k8s.gcr.io/pause --sort-by=~tags # # If you update this, also update scheduling.userPlaceholder.image.tag - tag: "3.5" + tag: "3.6" pullPolicy: pullSecrets: [] From 20c9028ec13d03f43e4ca60c49fb0c392b9fc455 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:28:34 +0100 Subject: [PATCH 135/898] bump: jupyterhub 2.0.0 to 2.0.1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 8c3013aad9..3e9ef28b0a 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.0 +jupyterhub==2.0.1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 398a1725a6..e4bd43ec86 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.0 +jupyterhub==2.0.1 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 022a984004..fe3613c04b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.0 +jupyterhub==2.0.1 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index ecea77f00b..472a1e3fce 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.0 +appVersion: 2.0.1 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 039d76c6e1830bfbb8730156048cac5d319c8b98 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:32:24 +0100 Subject: [PATCH 136/898] ci: remove workaround installing six --- .github/workflows/vuln-scan.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 24f3b47b10..f49d796de5 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -55,10 +55,7 @@ jobs: - name: Install chartpress run: | - # FIXME: six is required by docker 5.0.0 but isn't explicitly required - # https://github.com/docker/docker-py/issues/2807 - # - pip install chartpress six + pip install chartpress # charpress --list-images output lines of name:tag format. We use it with # a search string in matrix.image_ref to find the specific image to scan From c156243b437b01788230d89c7b4b58465068a173 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:30:48 +0100 Subject: [PATCH 137/898] Remove workaround to have PriorityClass resources as helm hooks --- jupyterhub/templates/scheduling/priorityclass.yaml | 13 ------------- .../scheduling/user-placeholder/priorityclass.yaml | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/jupyterhub/templates/scheduling/priorityclass.yaml b/jupyterhub/templates/scheduling/priorityclass.yaml index 3f7d8b84a9..77c84cbffc 100644 --- a/jupyterhub/templates/scheduling/priorityclass.yaml +++ b/jupyterhub/templates/scheduling/priorityclass.yaml @@ -4,22 +4,9 @@ kind: PriorityClass metadata: name: {{ include "jupyterhub.priority.fullname" . }} annotations: - # FIXME: PriorityClasses must be added before the other resources reference - # them, and in the past a workaround was needed to accomplish this: - # to make the resource a Helm hook. - # - # To transition this resource to no longer be a Helm hook resource, - # we explicitly add ownership annotations/labels (in 1.0.0) which - # will allow a future upgrade (in 2.0.0) to remove all hook and - # ownership annotations/labels. - # - helm.sh/hook: pre-install,pre-upgrade - helm.sh/hook-delete-policy: before-hook-creation - helm.sh/hook-weight: "-100" meta.helm.sh/release-name: "{{ .Release.Name }}" meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" labels: - app.kubernetes.io/managed-by: Helm {{- $_ := merge (dict "componentLabel" "default-priority") . }} {{- include "jupyterhub.labels" $_ | nindent 4 }} value: {{ .Values.scheduling.podPriority.defaultPriority }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml b/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml index 4466e339f1..bdedbddadc 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml @@ -5,22 +5,9 @@ kind: PriorityClass metadata: name: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} annotations: - # FIXME: PriorityClasses must be added before the other resources reference - # them, and in the past a workaround was needed to accomplish this: - # to make the resource a Helm hook. - # - # To transition this resource to no longer be a Helm hook resource, - # we explicitly add ownership annotations/labels (in 1.0.0) which - # will allow a future upgrade (in 2.0.0) to remove all hook and - # ownership annotations/labels. - # - helm.sh/hook: pre-install,pre-upgrade - helm.sh/hook-delete-policy: before-hook-creation - helm.sh/hook-weight: "-100" meta.helm.sh/release-name: "{{ .Release.Name }}" meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" labels: - app.kubernetes.io/managed-by: Helm {{- include "jupyterhub.labels" . | nindent 4 }} value: {{ .Values.scheduling.podPriority.userPlaceholderPriority }} globalDefault: false From 9b546a54afc882477fcebb9621fb4203715a26e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Dec 2021 00:07:37 +0000 Subject: [PATCH 138/898] build(deps): bump psycopg2-binary from 2.9.2 to 2.9.3 in /images/hub Bumps [psycopg2-binary](https://github.com/psycopg/psycopg2) from 2.9.2 to 2.9.3. - [Release notes](https://github.com/psycopg/psycopg2/releases) - [Changelog](https://github.com/psycopg/psycopg2/blob/master/NEWS) - [Commits](https://github.com/psycopg/psycopg2/commits) --- updated-dependencies: - dependency-name: psycopg2-binary dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e4bd43ec86..7911128717 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -123,7 +123,7 @@ pem==21.2.0 # via jupyterhub-ltiauthenticator prometheus-client==0.11.0 # via jupyterhub -psycopg2-binary==2.9.2 +psycopg2-binary==2.9.3 # via -r requirements.in py-spy==0.3.11 # via -r requirements.in From 53503b6b317ee3fae2be91e27ce9cd00029fd76e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jan 2022 21:06:19 +0000 Subject: [PATCH 139/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.29.1 → v2.31.0](https://github.com/asottile/pyupgrade/compare/v2.29.1...v2.31.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 67aa19883c..db5069386d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.31.0 hooks: - id: pyupgrade args: From 5b1afa16c53d762e019de0ec1c06633b80e2d098 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jan 2022 05:05:38 +0000 Subject: [PATCH 140/898] build(deps): bump aquasecurity/trivy-action from 0.2.0 to 0.2.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.0 to 0.2.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0...8f4c7160b470bafe4299efdc1c8a1fb495f8325a) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index f49d796de5..d218921baf 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 + uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 + uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@0769bbf0d2a77b3c15b3b57fbcdd2edd25a1c3f0 + uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a with: image-ref: rebuilt-image format: table From 9da8568d38a3127358ff4ece0ee2280f113d6aef Mon Sep 17 00:00:00 2001 From: Ray Bell Date: Wed, 5 Jan 2022 22:05:00 -0500 Subject: [PATCH 141/898] DOC: IAM definition --- docs/source/kubernetes/amazon/step-zero-aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/amazon/step-zero-aws.md b/docs/source/kubernetes/amazon/step-zero-aws.md index 715da7dd75..d8e21df53c 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws.md +++ b/docs/source/kubernetes/amazon/step-zero-aws.md @@ -11,7 +11,7 @@ template you will use to setup and shape your cluster. ## The Procedure -1. Create an IAM Role +1. Create an IAM (Identity and Access Managemen) Role. This role will be used to give your CI host permission to create and destroy resources on AWS. Instructions for creating a role can be found From 64bf6ea0838b6fff8047cd6cde59de8583cc6191 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 6 Jan 2022 04:21:39 +0100 Subject: [PATCH 142/898] Apply suggestions from code review --- docs/source/kubernetes/amazon/step-zero-aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/amazon/step-zero-aws.md b/docs/source/kubernetes/amazon/step-zero-aws.md index d8e21df53c..f873c81b18 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws.md +++ b/docs/source/kubernetes/amazon/step-zero-aws.md @@ -11,7 +11,7 @@ template you will use to setup and shape your cluster. ## The Procedure -1. Create an IAM (Identity and Access Managemen) Role. +1. Create an IAM (Identity and Access Management) Role. This role will be used to give your CI host permission to create and destroy resources on AWS. Instructions for creating a role can be found From fafb144aef3b60c2f4c65e17f6d030c1aa100b08 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sat, 8 Jan 2022 01:10:35 +0000 Subject: [PATCH 143/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 9862cbeae0..fe1a00be64 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2021-11-25_01:05:45 +# VULN_SCAN_TIME=2022-01-08_01:10:33 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From b788f326d30c6a235cba22ac460c2971d22db2d9 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 14 Jan 2022 01:12:30 +0000 Subject: [PATCH 144/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index c577ad38c5..702384804c 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2021-12-14_01:10:06 +# VULN_SCAN_TIME=2022-01-14_01:12:28 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From eafaf777bc46c92c87c8099d0c961598295336f0 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 14 Jan 2022 01:13:17 +0000 Subject: [PATCH 145/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index fe1a00be64..ce8b054e2b 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-01-08_01:10:33 +# VULN_SCAN_TIME=2022-01-14_01:13:15 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From f706eaa28e173823f69f6217ba1a15c0a625c50a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 14 Jan 2022 09:43:05 +0100 Subject: [PATCH 146/898] bump JupyterHub from 2.0.1 to 2.0.2 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 3e9ef28b0a..78df8828ef 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.1 +jupyterhub==2.0.2 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7911128717..6f0b259472 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.1 +jupyterhub==2.0.2 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index fe3613c04b..9108081cec 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.1 +jupyterhub==2.0.2 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 472a1e3fce..629c3717e1 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.1 +appVersion: 2.0.2 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From f06c40fa77c5b371be25afb149d62add69370cb7 Mon Sep 17 00:00:00 2001 From: "Mark.lee" Date: Fri, 14 Jan 2022 18:04:26 +0800 Subject: [PATCH 147/898] Replace zone to allowedTopologies Restrict the topology of provisioned volumes to specific zones --- docs/source/jupyterhub/customizing/user-storage.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-storage.md b/docs/source/jupyterhub/customizing/user-storage.md index 7ea48fad15..3d04f194b1 100644 --- a/docs/source/jupyterhub/customizing/user-storage.md +++ b/docs/source/jupyterhub/customizing/user-storage.md @@ -138,7 +138,11 @@ metadata: provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd - zones: +allowedTopologies: +- matchLabelExpressions: + - key: failure-domain.beta.kubernetes.io/zone + values: + - ``` Replace `` with the Zone in which you created your cluster (you can find From 9b6668c754a5f7985b692c8ecad0e3d7f63a0b9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 10:07:28 +0000 Subject: [PATCH 148/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/jupyterhub/customizing/user-storage.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-storage.md b/docs/source/jupyterhub/customizing/user-storage.md index 3d04f194b1..4c9d4d74ae 100644 --- a/docs/source/jupyterhub/customizing/user-storage.md +++ b/docs/source/jupyterhub/customizing/user-storage.md @@ -139,10 +139,10 @@ provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd allowedTopologies: -- matchLabelExpressions: - - key: failure-domain.beta.kubernetes.io/zone - values: - - + - matchLabelExpressions: + - key: failure-domain.beta.kubernetes.io/zone + values: + - ``` Replace `` with the Zone in which you created your cluster (you can find From 5de38dda59eb93793597f6fc8f0f62b622c76629 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sat, 15 Jan 2022 17:40:18 +0000 Subject: [PATCH 149/898] Use chart logo from this repo --- docs/source/_static/images/logo/hublogo.svg | 75 +++++++++++++++++++++ jupyterhub/Chart.yaml | 2 +- 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 docs/source/_static/images/logo/hublogo.svg diff --git a/docs/source/_static/images/logo/hublogo.svg b/docs/source/_static/images/logo/hublogo.svg new file mode 100644 index 0000000000..f000235fa2 --- /dev/null +++ b/docs/source/_static/images/logo/hublogo.svg @@ -0,0 +1,75 @@ + + + +Group Copy 2 +Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 629c3717e1..3dc871cf87 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -7,7 +7,7 @@ description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] -icon: https://jupyter.org/assets/hublogo.svg +icon: https://zero-to-jupyterhub.readthedocs.io/en/latest/_static/hublogo.svg kubeVersion: ">=1.17.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers From 98df189c5c46e6f99b4ef10aed3c59d67b547f38 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 19 Jan 2022 00:52:43 +0100 Subject: [PATCH 150/898] Enable imagePullSecrets helper to be used by parent charts --- jupyterhub/templates/_helpers.tpl | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl index 10b31c7058..ad7f421aa6 100644 --- a/jupyterhub/templates/_helpers.tpl +++ b/jupyterhub/templates/_helpers.tpl @@ -180,9 +180,30 @@ component: {{ include "jupyterhub.componentLabel" . }} Augments passed .pullSecrets with $.Values.imagePullSecrets */}} {{- define "jupyterhub.imagePullSecrets" -}} + {{- /* + We have implemented a trick to allow a parent chart depending on this + chart to call this named templates. + + Caveats and notes: + + 1. While parent charts can reference these, grandparent charts can't. + 2. Parent charts must not use an alias for this chart. + 3. There is no failsafe workaround to above due to + https://github.com/helm/helm/issues/9214. + 4. .Chart is of its own type (*chart.Metadata) and needs to be casted + using "toYaml | fromYaml" in order to be able to use normal helm + template functions on it. + */}} + {{- $jupyterhub_values := .root.Values }} + {{- if ne .root.Chart.Name "jupyterhub" }} + {{- if .root.Values.jupyterhub }} + {{- $jupyterhub_values = .root.Values.jupyterhub }} + {{- end }} + {{- end }} + {{- /* Populate $_.list with all relevant entries */}} -{{- $_ := dict "list" (concat .image.pullSecrets .root.Values.imagePullSecrets | uniq) }} -{{- if and .root.Values.imagePullSecret.create .root.Values.imagePullSecret.automaticReferenceInjection }} +{{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }} +{{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }} {{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }} {{- end }} From f461409a06551d2d44fecd142decd7787bcac26a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 19 Jan 2022 00:53:28 +0100 Subject: [PATCH 151/898] Add indentation for improved readability --- jupyterhub/templates/_helpers.tpl | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl index ad7f421aa6..eecdc8995b 100644 --- a/jupyterhub/templates/_helpers.tpl +++ b/jupyterhub/templates/_helpers.tpl @@ -201,30 +201,30 @@ component: {{ include "jupyterhub.componentLabel" . }} {{- end }} {{- end }} -{{- /* Populate $_.list with all relevant entries */}} -{{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }} -{{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }} -{{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }} -{{- end }} - -{{- /* Decide if something should be written */}} -{{- if not (eq ($_.list | toJson) "[]") }} + {{- /* Populate $_.list with all relevant entries */}} + {{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }} + {{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }} + {{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }} + {{- end }} -{{- /* Process the $_.list where strings become dicts with a name key and the -strings become the name keys' values into $_.res */}} -{{- $_ := set $_ "res" list }} -{{- range $_.list }} -{{- if eq (typeOf .) "string" }} -{{- $__ := set $_ "res" (append $_.res (dict "name" .)) }} -{{- else }} -{{- $__ := set $_ "res" (append $_.res .) }} -{{- end }} -{{- end }} + {{- /* Decide if something should be written */}} + {{- if not (eq ($_.list | toJson) "[]") }} + + {{- /* Process the $_.list where strings become dicts with a name key and the + strings become the name keys' values into $_.res */}} + {{- $_ := set $_ "res" list }} + {{- range $_.list }} + {{- if eq (typeOf .) "string" }} + {{- $__ := set $_ "res" (append $_.res (dict "name" .)) }} + {{- else }} + {{- $__ := set $_ "res" (append $_.res .) }} + {{- end }} + {{- end }} -{{- /* Write the results */}} -{{- $_.res | toJson }} + {{- /* Write the results */}} + {{- $_.res | toJson }} -{{- end }} + {{- end }} {{- end }} {{- /* From d6d0ddc0ebd19b05629d0ea3eba45c5dd72e9b6a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 19 Jan 2022 03:16:38 +0100 Subject: [PATCH 152/898] docs: fix broken anchor in link --- jupyterhub/schema.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 414c5da76d..fb142d9819 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -429,7 +429,7 @@ properties: some custom way. For more details, see the [Kubernetes - documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes). + documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/). args: type: array description: | @@ -472,7 +472,7 @@ properties: ``` For more details, see the [Kubernetes - documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes). + documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/). cookieSecret: type: [string, "null"] description: | From dd98546457c7ce240d306bff9d5f19097d101ad8 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Thu, 9 Dec 2021 12:57:09 +0530 Subject: [PATCH 153/898] Block private network access for singleuser pods by default This PR enables all traffic to the *internet* from user pods, but restricts all access to the private network where other kubernetes pods or services may be running. Meant to be a minimally intrusive defense in depth mechanism that prevents access to unsecured endpoints running on VMs, in the kubernetes cluster, etc. For example, users often run Prometheus without any kind of network protection in the same cluster as the users, and access to prometheus can leak a lot of sensitive user data. https://www.wiz.io/blog/chaosdb-explained-azures-cosmos-db-vulnerability-walkthrough shows the immense security value of restricting access to internal network from user written code. Most software projects do not have their authz/authn layers, and rely on network segmentation for protection. When network segmentation fails like it does in the Azure security incident, there is often no secondary line of defense. Most JupyterHub users talk to the internet, but by default I think nobody really talks to the *internal network*. This also only affects network connections from user code - stuff like mounting NFS shares are done by the kubelet, and hence are not affected. Connections to hub infrastructure (proxy, hub, etc) are also not affected, as they are separately allowed by other NetworkPolicy rules. --- jupyterhub/templates/singleuser/netpol.yaml | 18 ++++++++++++++++++ jupyterhub/values.yaml | 1 + 2 files changed, 19 insertions(+) diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 6f3d44debb..9c704a4460 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -77,6 +77,24 @@ spec: - protocol: TCP port: 53 + {{- if .Values.singleuser.networkPolicy.noPrivateIPAccess }} + # Allow broad access to the external internet, but block all + # access to private IPs. This means users can access the internet but not + # (say) an unsecured prometheus server running in the same cluster. Given + # that not all charts have proper NetworkPolicies setup, this is a defense + # in depth measure that restricts the amount of damage someone could do if + # they get an unauthorized login into the hub system. + - to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + # Don't allow network access to private IP ranges + # Listed in https://datatracker.ietf.org/doc/html/rfc1918 + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + {{- end }} + {{- with .Values.singleuser.networkPolicy.egress }} # singleuser-server --> depends, but the default is everything {{- . | toYaml | nindent 4 }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c1daf020ab..b99ba448a6 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -331,6 +331,7 @@ singleuser: networkPolicy: enabled: true ingress: [] + noPrivateIPAccess: true egress: # Required egress to communicate with the hub and DNS servers will be # augmented to these egress rules. From 108d3297fa2ce0c14cdb61eb652e5aa0ff6c34b2 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Thu, 9 Dec 2021 13:12:07 +0530 Subject: [PATCH 154/898] Explicitly allow user -> proxy/autohttps traffic --- jupyterhub/templates/singleuser/netpol.yaml | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 9c704a4460..035e01ef74 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -77,6 +77,29 @@ spec: - protocol: TCP port: 53 + # User code should *always* be able to access the public facing URL + # of the hub. When internal network access is blocked, NetworkPolicy + # will not allow code to request https:// from + # user code. This can break hub services. + # We explicitly allow this outbound access regardless of wether + # we are using autohttps to provide HTTPS or not. + - to: + - podSelector: + matchLabels: + app: jupyterhub + component: autohttps + + # Library code (such as dask-gateway) often wants a way to refer + # to hub services without the user having to supply the domain the + # hub is hosted at. Since the CHP proxy pod will always be running + # and routing hub services, they use http://proxy-public as a URL + # to route to hub services. This NetworkPolicy rule explicitly + # allows that. + - podSelector: + matchLabels: + app: jupyterhub + component: proxy + {{- if .Values.singleuser.networkPolicy.noPrivateIPAccess }} # Allow broad access to the external internet, but block all # access to private IPs. This means users can access the internet but not From e272d6b26b0c3f129f5f8789d7e31be793fd008b Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 14 Dec 2021 20:02:43 +0530 Subject: [PATCH 155/898] Don't hardcode label selectors for network policy --- jupyterhub/templates/singleuser/netpol.yaml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 035e01ef74..9d6195a9b8 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -86,9 +86,8 @@ spec: - to: - podSelector: matchLabels: - app: jupyterhub - component: autohttps - + {{- $_ := merge (dict "componentLabel" "autohttps") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} # Library code (such as dask-gateway) often wants a way to refer # to hub services without the user having to supply the domain the # hub is hosted at. Since the CHP proxy pod will always be running @@ -97,8 +96,8 @@ spec: # allows that. - podSelector: matchLabels: - app: jupyterhub - component: proxy + {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} {{- if .Values.singleuser.networkPolicy.noPrivateIPAccess }} # Allow broad access to the external internet, but block all From 54231a5cd39c0e11fcbf38d9249bdea3090a03bc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 18 Dec 2021 17:01:50 +0100 Subject: [PATCH 156/898] WIP: initial pass --- jupyterhub/templates/singleuser/netpol.yaml | 83 +++++++++++++-------- jupyterhub/values.yaml | 18 ++--- 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 9d6195a9b8..0985ef1f1d 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -62,50 +62,69 @@ spec: egress: # singleuser-server --> hub - - ports: - - port: 8081 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "hub") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8081 - # singleuser-server --> Kubernetes internal DNS - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - - # User code should *always* be able to access the public facing URL - # of the hub. When internal network access is blocked, NetworkPolicy - # will not allow code to request https:// from - # user code. This can break hub services. - # We explicitly allow this outbound access regardless of wether - # we are using autohttps to provide HTTPS or not. + # singleuser-server --> proxy + # singleuser-server --> autohttps + # + # While not critical for core functionality, a user or library code may rely + # on communicating with the proxy or autohttps pods via a k8s Service it can + # detected from well known environment variables. + # - to: - podSelector: matchLabels: - {{- $_ := merge (dict "componentLabel" "autohttps") . }} + {{- $_ := merge (dict "componentLabel" "proxy") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - # Library code (such as dask-gateway) often wants a way to refer - # to hub services without the user having to supply the domain the - # hub is hosted at. Since the CHP proxy pod will always be running - # and routing hub services, they use http://proxy-public as a URL - # to route to hub services. This NetworkPolicy rule explicitly - # allows that. + ports: + - port: 8000 + - to: - podSelector: matchLabels: - {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- $_ := merge (dict "componentLabel" "autohttps") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8080 + - port: 8443 + + {{- if .Values.singleuser.networkPolicy.egressAllowRules.dnsPorts }} + # singleuser-server --> DNS ports + # + # We can't reliably identify the Kubernetes internal DNS server because that + # varies too much between k8s clusters. Due to that, we allow all + # connections to port 53. + # + - ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + {{- end }} + + {{- if .Values.singleuser.networkPolicy.egressAllowRules.cloudMetadataServer }} + # singleuser-server --> cloud metadata server + - to: + - ipBlock: + cidr: {{ .Values.cloudMetadata.ip }}/32 + {{- end }} - {{- if .Values.singleuser.networkPolicy.noPrivateIPAccess }} - # Allow broad access to the external internet, but block all - # access to private IPs. This means users can access the internet but not - # (say) an unsecured prometheus server running in the same cluster. Given - # that not all charts have proper NetworkPolicies setup, this is a defense - # in depth measure that restricts the amount of damage someone could do if - # they get an unauthorized login into the hub system. + {{- if .Values.singleuser.networkPolicy.egressAllowRules.publicIPs }} + # singleuser-server --> public IPs (but not the cloud metadata server) + # + # Allow access to the external internet, but don't allow access to private + # IPs or the cloud metadata server. This means users can access the internet + # but not (say) an unsecured prometheus server running in the same cluster. + # + # Given that not all charts have proper NetworkPolicies setup, this is a + # defense in depth measure that restricts the amount of damage someone could + # do if they get an unauthorized login into the hub system. + # - to: - ipBlock: cidr: 0.0.0.0/0 @@ -115,6 +134,8 @@ spec: - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 + # Don't allow access to the cloud metadata server + - {{ .Values.cloudMetadata.ip }}/32 {{- end }} {{- with .Values.singleuser.networkPolicy.egress }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index b99ba448a6..16e538c05d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -331,19 +331,11 @@ singleuser: networkPolicy: enabled: true ingress: [] - noPrivateIPAccess: true - egress: - # Required egress to communicate with the hub and DNS servers will be - # augmented to these egress rules. - # - # This default rule explicitly allows all outbound traffic from singleuser - # pods, except to a typical IP used to return metadata that can be used by - # someone with malicious intent. - - to: - - ipBlock: - cidr: 0.0.0.0/0 - except: - - 169.254.169.254/32 + egressAllowRules: + cloudMetadataServer: false + dnsPorts: true + publicIPs: true + egress: [] interNamespaceAccessLabels: ignore allowedIngressPorts: [] events: true From da47d0faeda594e6d771e7775a03d9570a1f83db Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 20 Dec 2021 19:37:52 +0100 Subject: [PATCH 157/898] WIP: second pass --- jupyterhub/schema.yaml | 77 ++++++++++++++++++- jupyterhub/templates/_helpers-netpol.tpl | 73 ++++++++++++++++++ jupyterhub/templates/hub/netpol.yaml | 25 +++--- .../templates/proxy/autohttps/netpol.yaml | 18 ++--- jupyterhub/templates/proxy/netpol.yaml | 24 ++---- jupyterhub/templates/singleuser/netpol.yaml | 50 +----------- jupyterhub/values.yaml | 41 +++++----- 7 files changed, 194 insertions(+), 114 deletions(-) create mode 100644 jupyterhub/templates/_helpers-netpol.tpl diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fb142d9819..02accdffa7 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -583,11 +583,86 @@ properties: egress: type: array description: | - Additional egress rules to add except those that is known to be needed by the respective pods in the Helm chart. + Additional egress rules to add except those that is known to be needed by the respective pods in the Helm chart. The default value of this egress is to allow all traffic, except for the `singleuser.networkPolicy.egress`, which is also limiting access to a metadata server that can be exploited. If you want to restrict egress, you can override this permissive default to be an empty list. + egressAllowRules: + type: object + additionalProperties: false + description: | + This is a set of predefined rules that when enabled will be added + to the NetworkPolicy list of egress rules. + + The resulting egress rules will be a composition of: + - rules specific for the respective pod(s) function within the + Helm chart + - rules based on enabled `egressAllowRules` flags + - rules explicitly specified by the user + + ```{note} + Each flag under this configuration will not render into a + dedicated rule in the NetworkPolicy resource, but instead combine + with the other flags to a reduced set of rules to avoid a + performance penalty. + ``` + properties: + cloudMetadataServer: + type: boolean + description: | + Defaults to `false` for singleuser servers, but to `true` for + all other network policies. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to the cloud metadata server. + + Note that the `nonPrivateIPs` rule is allowing all non Private + IP ranges but makes an exception for the cloud metadata + server, leaving this as the definitive configuration to allow + access to the cloud metadata server. + dnsPortsPrivateIPs: + type: boolean + description: | + Defaults to `true` for all network policies. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to private IPs via port 53. + + Note that we can't reliably identify the k8s internal DNS + server due to variations between k8s clusters. Due to that, + this rule which is critical for core functionality, can be + disabled for a more refined custom rule. + nonPrivateIPs: + type: boolean + description: | + Defaults to `true` for all network policies. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to the non-private IP ranges + with the exception of the cloud metadata server. This means + respective pod(s) can establish connections to the internet + but not (say) an unsecured prometheus server running in the + same cluster. + privateIPs: + type: boolean + description: | + Defaults to `false` for singleuser servers, but to `true` for + all other network policies. + + Private IPs refer to the IP ranges `10.0.0.0/8`, + `172.16.0.0/12`, `192.168.0.0/16`. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to the internal k8s cluster. + This means users can access the internet but not (say) an + unsecured prometheus server running in the same cluster. + + Since not all workloads in the k8s cluster may have + NetworkPolicies setup to restrict their incoming connections, + having this set to false can be a good defence against + malicious intent from someone in control of software in these + pods. interNamespaceAccessLabels: enum: [accept, ignore] description: | diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl new file mode 100644 index 0000000000..9e17c2d3ea --- /dev/null +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -0,0 +1,73 @@ +{{- define "jupyterhub.networkPolicy.standardEgressConfiguration" -}} +{{- include "jupyterhub.networkPolicy.standardEgressConfiguration.withLeadingNewLine" . | trimPrefix "\n" }} +{{- end }} + +{{- define "jupyterhub.networkPolicy.standardEgressConfiguration.withLeadingNewLine" -}} +{{- $root := index . 0 }} +{{- $netpol := index . 1 }} +{{- if and ($netpol.egressAllowRules.dnsPortsPrivateIPs) (not $netpol.egressAllowRules.privateIPs) }} +# Allow outbound connections to the DNS port in the private IP ranges +- ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + to: + - ipBlock: + cidr: 10.0.0.0/8 + - ipBlock: + cidr: 172.16.0.0/12 + - ipBlock: + cidr: 192.168.0.0/16 +{{- end }} + +{{- if $netpol.egressAllowRules.nonPrivateIPs }} +# Allow outbound connections to non-private IP ranges +{{- if $netpol.egressAllowRules.privateIPs }} +# Allow outbound connections to private IP ranges (and their DNS ports) +{{- end }} +{{- if $netpol.egressAllowRules.cloudMetadataServer }} +# Allow outbound connections to cloud metadata server +{{- end }} +- to: + - ipBlock: + cidr: 0.0.0.0/0 + {{- if or (not $netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.cloudMetadataServer) }} + except: + {{- if not $netpol.egressAllowRules.privateIPs }} + # Don't allow outbound connections to private IP ranges + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + {{- end }} + + {{- if not $netpol.egressAllowRules.cloudMetadataServer }} + # Don't allow access to the cloud metadata server + - {{ $root.Values.singleuser.cloudMetadata.ip }}/32 + {{- end }} + {{- end }} +{{- end }} + +{{- if and ($netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.nonPrivateIPs) }} +# Allow outbound connections to private IP ranges (and their DNS ports) +- to: + - ipBlock: + cidr: 10.0.0.0/8 + - ipBlock: + cidr: 172.16.0.0/12 + - ipBlock: + cidr: 192.168.0.0/16 +{{- end }} + +{{- if and ($netpol.egressAllowRules.cloudMetadataServer) (not $netpol.egressAllowRules.nonPrivateIPs) }} +# Allow outbound connections to cloud metadata server +- to: + - ipBlock: + cidr: {{ $root.Values.singleuser.cloudMetadata.ip }}/32 +{{- end }} + +{{- with $netpol.egress }} +# Allow outbound connections based on user specified rules +{{- . | toYaml }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/hub/netpol.yaml b/jupyterhub/templates/hub/netpol.yaml index 0ba091963d..6f3dae7d63 100644 --- a/jupyterhub/templates/hub/netpol.yaml +++ b/jupyterhub/templates/hub/netpol.yaml @@ -61,31 +61,24 @@ spec: egress: # hub --> proxy - - ports: - - port: 8001 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "proxy") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8001 + # hub --> singleuser-server - - ports: - - port: 8888 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8888 - # hub --> Kubernetes internal DNS - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - - {{- with .Values.hub.networkPolicy.egress }} - # hub --> depends, but the default is everything - {{- . | toYaml | nindent 4 }} + {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.hub.networkPolicy)) }} + {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/netpol.yaml b/jupyterhub/templates/proxy/autohttps/netpol.yaml index 50b7fe2eb5..7078f7800f 100644 --- a/jupyterhub/templates/proxy/autohttps/netpol.yaml +++ b/jupyterhub/templates/proxy/autohttps/netpol.yaml @@ -64,23 +64,15 @@ spec: egress: # autohttps --> proxy (http port) - - ports: - - port: 8000 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "proxy") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8000 - # autohttps --> Kubernetes internal DNS - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - - {{- with .Values.proxy.traefik.networkPolicy.egress }} - # autohttps --> depends, but the default is everything - {{- . | toYaml | nindent 4 }} + {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.proxy.traefik.networkPolicy)) }} + {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/proxy/netpol.yaml b/jupyterhub/templates/proxy/netpol.yaml index 465112f8d0..9698e0da16 100644 --- a/jupyterhub/templates/proxy/netpol.yaml +++ b/jupyterhub/templates/proxy/netpol.yaml @@ -85,32 +85,24 @@ spec: egress: # proxy --> hub - - ports: - - port: 8081 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "hub") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8081 # proxy --> singleuser-server - - ports: - - port: 8888 - to: + - to: - podSelector: matchLabels: {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8888 - # proxy --> Kubernetes internal DNS - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - - {{- with .Values.proxy.chp.networkPolicy.egress }} - # proxy --> depends, but the default is everything - {{- . | toYaml | nindent 4 }} + {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.proxy.chp.networkPolicy)) }} + {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 0985ef1f1d..ca14500e2a 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -93,53 +93,7 @@ spec: - port: 8080 - port: 8443 - {{- if .Values.singleuser.networkPolicy.egressAllowRules.dnsPorts }} - # singleuser-server --> DNS ports - # - # We can't reliably identify the Kubernetes internal DNS server because that - # varies too much between k8s clusters. Due to that, we allow all - # connections to port 53. - # - - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - {{- end }} - - {{- if .Values.singleuser.networkPolicy.egressAllowRules.cloudMetadataServer }} - # singleuser-server --> cloud metadata server - - to: - - ipBlock: - cidr: {{ .Values.cloudMetadata.ip }}/32 - {{- end }} - - {{- if .Values.singleuser.networkPolicy.egressAllowRules.publicIPs }} - # singleuser-server --> public IPs (but not the cloud metadata server) - # - # Allow access to the external internet, but don't allow access to private - # IPs or the cloud metadata server. This means users can access the internet - # but not (say) an unsecured prometheus server running in the same cluster. - # - # Given that not all charts have proper NetworkPolicies setup, this is a - # defense in depth measure that restricts the amount of damage someone could - # do if they get an unauthorized login into the hub system. - # - - to: - - ipBlock: - cidr: 0.0.0.0/0 - except: - # Don't allow network access to private IP ranges - # Listed in https://datatracker.ietf.org/doc/html/rfc1918 - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - # Don't allow access to the cloud metadata server - - {{ .Values.cloudMetadata.ip }}/32 - {{- end }} - - {{- with .Values.singleuser.networkPolicy.egress }} - # singleuser-server --> depends, but the default is everything - {{- . | toYaml | nindent 4 }} + {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.singleuser.networkPolicy)) }} + {{ . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 16e538c05d..327d7a5791 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -97,16 +97,12 @@ hub: networkPolicy: enabled: true ingress: [] - ## egress for JupyterHub already includes Kubernetes internal DNS and - ## access to the proxy, but can be restricted further, but ensure to allow - ## access to the Kubernetes API server that couldn't be pinned ahead of - ## time. - ## - ## ref: https://stackoverflow.com/a/59016417/2220152 - egress: - - to: - - ipBlock: - cidr: 0.0.0.0/0 + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true interNamespaceAccessLabels: ignore allowedIngressPorts: [] allowNamedServers: false @@ -219,10 +215,12 @@ proxy: networkPolicy: enabled: true ingress: [] - egress: - - to: - - ipBlock: - cidr: 0.0.0.0/0 + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true interNamespaceAccessLabels: ignore allowedIngressPorts: [http, https] pdb: @@ -259,10 +257,12 @@ proxy: networkPolicy: enabled: true ingress: [] - egress: - - to: - - ipBlock: - cidr: 0.0.0.0/0 + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true interNamespaceAccessLabels: ignore allowedIngressPorts: [http, https] pdb: @@ -333,8 +333,9 @@ singleuser: ingress: [] egressAllowRules: cloudMetadataServer: false - dnsPorts: true - publicIPs: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: false egress: [] interNamespaceAccessLabels: ignore allowedIngressPorts: [] From de1374f6b78d0224fd40715859d323bb8cabc404 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 19:09:01 +0100 Subject: [PATCH 158/898] Add comment and rename named helm template --- jupyterhub/templates/_helpers-netpol.tpl | 36 ++++++++++++++++--- jupyterhub/templates/hub/netpol.yaml | 2 +- .../templates/proxy/autohttps/netpol.yaml | 2 +- jupyterhub/templates/proxy/netpol.yaml | 2 +- jupyterhub/templates/singleuser/netpol.yaml | 2 +- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 9e17c2d3ea..7b284c4868 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -1,11 +1,35 @@ -{{- define "jupyterhub.networkPolicy.standardEgressConfiguration" -}} -{{- include "jupyterhub.networkPolicy.standardEgressConfiguration.withLeadingNewLine" . | trimPrefix "\n" }} -{{- end }} +{{- /* + This named template renders egress rules for NetworkPolicy resources based on + common configuration. + + It is rendering based on the `egressAllowRules` and `egress` keys of the + passed networkPolicy config object. In practice each flag under + egressAllowRules could be a dedicated egress rule, but for potential + performance impact we have increased the complexity of this rendering logic + and render them in a coalesced form. + + This named template needs to render based on a specific networkPolicy + resource, but also needs access to the root context. Due to that, it + accepts a list as its scope, where the first element is supposed to be the + root context and the second element is supposed to be the networkPolicy + configuration object. + + As an example, this is how you would render this named template from a + NetworkPolicy resource under its egress: + + egress: + # other rules here... + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} +*/}} -{{- define "jupyterhub.networkPolicy.standardEgressConfiguration.withLeadingNewLine" -}} +{{- define "jupyterhub.networkPolicy.renderEgressRules" -}} {{- $root := index . 0 }} {{- $netpol := index . 1 }} {{- if and ($netpol.egressAllowRules.dnsPortsPrivateIPs) (not $netpol.egressAllowRules.privateIPs) }} + # Allow outbound connections to the DNS port in the private IP ranges - ports: - protocol: UDP @@ -22,6 +46,7 @@ {{- end }} {{- if $netpol.egressAllowRules.nonPrivateIPs }} + # Allow outbound connections to non-private IP ranges {{- if $netpol.egressAllowRules.privateIPs }} # Allow outbound connections to private IP ranges (and their DNS ports) @@ -49,6 +74,7 @@ {{- end }} {{- if and ($netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.nonPrivateIPs) }} + # Allow outbound connections to private IP ranges (and their DNS ports) - to: - ipBlock: @@ -60,6 +86,7 @@ {{- end }} {{- if and ($netpol.egressAllowRules.cloudMetadataServer) (not $netpol.egressAllowRules.nonPrivateIPs) }} + # Allow outbound connections to cloud metadata server - to: - ipBlock: @@ -67,6 +94,7 @@ {{- end }} {{- with $netpol.egress }} + # Allow outbound connections based on user specified rules {{- . | toYaml }} {{- end }} diff --git a/jupyterhub/templates/hub/netpol.yaml b/jupyterhub/templates/hub/netpol.yaml index 6f3dae7d63..904b2c32b0 100644 --- a/jupyterhub/templates/hub/netpol.yaml +++ b/jupyterhub/templates/hub/netpol.yaml @@ -78,7 +78,7 @@ spec: ports: - port: 8888 - {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.hub.networkPolicy)) }} + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/netpol.yaml b/jupyterhub/templates/proxy/autohttps/netpol.yaml index 7078f7800f..78270b696d 100644 --- a/jupyterhub/templates/proxy/autohttps/netpol.yaml +++ b/jupyterhub/templates/proxy/autohttps/netpol.yaml @@ -72,7 +72,7 @@ spec: ports: - port: 8000 - {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.proxy.traefik.networkPolicy)) }} + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.traefik.networkPolicy)) }} {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/proxy/netpol.yaml b/jupyterhub/templates/proxy/netpol.yaml index 9698e0da16..0af853aa0d 100644 --- a/jupyterhub/templates/proxy/netpol.yaml +++ b/jupyterhub/templates/proxy/netpol.yaml @@ -102,7 +102,7 @@ spec: ports: - port: 8888 - {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.proxy.chp.networkPolicy)) }} + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.chp.networkPolicy)) }} {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index ca14500e2a..08db2caec7 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -93,7 +93,7 @@ spec: - port: 8080 - port: 8443 - {{- with (include "jupyterhub.networkPolicy.standardEgressConfiguration" (list . .Values.singleuser.networkPolicy)) }} + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.singleuser.networkPolicy)) }} {{ . | nindent 4 }} {{- end }} {{- end }} From 9698b7bbc3e4dec34300ca9c9dd65e8a48f4983f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 19:13:54 +0100 Subject: [PATCH 159/898] Add lint-and-validate values for new netpol config --- tools/templates/lint-and-validate-values.yaml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 3183354518..7b02fd71f4 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -174,6 +174,11 @@ hub: minAvailable: null networkPolicy: enabled: true + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: false egress: - to: - ipBlock: @@ -233,6 +238,11 @@ proxy: effect: NoSchedule networkPolicy: enabled: true + egressAllowRules: + cloudMetadataServer: false + dnsPortsPrivateIPs: false + nonPrivateIPs: false + privateIPs: true egress: - to: - ipBlock: @@ -257,6 +267,11 @@ proxy: effect: NoSchedule networkPolicy: enabled: true + egressAllowRules: + cloudMetadataServer: false + dnsPortsPrivateIPs: false + nonPrivateIPs: true + privateIPs: true egress: - to: - ipBlock: @@ -377,6 +392,11 @@ singleuser: ip: 169.254.169.254 networkPolicy: enabled: true + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: false + privateIPs: false egress: - to: - ipBlock: From 09b9c9a7c877efd6fbebfd5acc34cfdb21e13f3c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 19:36:32 +0100 Subject: [PATCH 160/898] Fix syntax details --- jupyterhub/templates/_helpers-netpol.tpl | 7 +------ jupyterhub/templates/singleuser/netpol.yaml | 2 +- jupyterhub/values.yaml | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 7b284c4868..6d8659f362 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -29,7 +29,6 @@ {{- $root := index . 0 }} {{- $netpol := index . 1 }} {{- if and ($netpol.egressAllowRules.dnsPortsPrivateIPs) (not $netpol.egressAllowRules.privateIPs) }} - # Allow outbound connections to the DNS port in the private IP ranges - ports: - protocol: UDP @@ -46,7 +45,6 @@ {{- end }} {{- if $netpol.egressAllowRules.nonPrivateIPs }} - # Allow outbound connections to non-private IP ranges {{- if $netpol.egressAllowRules.privateIPs }} # Allow outbound connections to private IP ranges (and their DNS ports) @@ -74,7 +72,6 @@ {{- end }} {{- if and ($netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.nonPrivateIPs) }} - # Allow outbound connections to private IP ranges (and their DNS ports) - to: - ipBlock: @@ -86,7 +83,6 @@ {{- end }} {{- if and ($netpol.egressAllowRules.cloudMetadataServer) (not $netpol.egressAllowRules.nonPrivateIPs) }} - # Allow outbound connections to cloud metadata server - to: - ipBlock: @@ -94,8 +90,7 @@ {{- end }} {{- with $netpol.egress }} - # Allow outbound connections based on user specified rules -{{- . | toYaml }} +{{ . | toYaml }} {{- end }} {{- end }} diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml index 08db2caec7..f388b81d3e 100644 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -94,6 +94,6 @@ spec: - port: 8443 {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.singleuser.networkPolicy)) }} - {{ . | nindent 4 }} + {{- . | nindent 4 }} {{- end }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 327d7a5791..f081892bb7 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -331,12 +331,12 @@ singleuser: networkPolicy: enabled: true ingress: [] + egress: [] egressAllowRules: cloudMetadataServer: false dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: false - egress: [] interNamespaceAccessLabels: ignore allowedIngressPorts: [] events: true From 607edb106609dfcca4009343553b1bedb1eb2fad Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 19:53:17 +0100 Subject: [PATCH 161/898] ci: fix test failure caused by allowing all nonPrivateIPs --- dev-config.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-config.yaml b/dev-config.yaml index 64478515ee..4c7f9332f9 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -147,7 +147,9 @@ singleuser: memory: guarantee: null networkPolicy: - # For testing purposes in test_singleuser_netpol + # For testing purposes in test_spawn_netpol, we override egress and egressAllowRules.nonPrivateIPs + egressAllowRules: + nonPrivateIPs: false egress: - to: # jupyter.org has multiple IPs associated with it, among them are these @@ -156,6 +158,7 @@ singleuser: cidr: 104.21.25.233/32 # - ipBlock: # cidr: 172.67.134.225/32 + extraEnv: TEST_ENV_FIELDREF_TO_NAMESPACE: valueFrom: From 59a08f150b122662040e7285ad7007fca09a565e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 20:01:17 +0100 Subject: [PATCH 162/898] ci: fix test failure due caused by upgrade tests --- dev-config-local-chart-extra-config.yaml | 3 +++ dev-config.yaml | 5 ++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml index 4c8827e266..d1c006a0c0 100644 --- a/dev-config-local-chart-extra-config.yaml +++ b/dev-config-local-chart-extra-config.yaml @@ -24,6 +24,9 @@ hub: groups: [] singleuser: + # FIXME: move resources to dev-config.yaml after 2.0.0 is released. + egressAllowRules: + nonPrivateIPs: false networkTools: # FIXME: move resources to dev-config.yaml after 2.0.0 is released. resources: diff --git a/dev-config.yaml b/dev-config.yaml index 4c7f9332f9..d92d4e8e71 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -147,9 +147,8 @@ singleuser: memory: guarantee: null networkPolicy: - # For testing purposes in test_spawn_netpol, we override egress and egressAllowRules.nonPrivateIPs - egressAllowRules: - nonPrivateIPs: false + # For testing purposes in the test_spawn_netpol test, we override egress and + # egressAllowRules.nonPrivateIPs to slim the egress rules to a minimum. egress: - to: # jupyter.org has multiple IPs associated with it, among them are these From c09e90b0513b432156a22f2303c1d3b824487e08 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 21 Dec 2021 20:56:39 +0100 Subject: [PATCH 163/898] ci: fix typo --- dev-config-local-chart-extra-config.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml index d1c006a0c0..8df40b769d 100644 --- a/dev-config-local-chart-extra-config.yaml +++ b/dev-config-local-chart-extra-config.yaml @@ -25,8 +25,9 @@ hub: singleuser: # FIXME: move resources to dev-config.yaml after 2.0.0 is released. - egressAllowRules: - nonPrivateIPs: false + networkPolicy: + egressAllowRules: + nonPrivateIPs: false networkTools: # FIXME: move resources to dev-config.yaml after 2.0.0 is released. resources: From 610a4fd3d7f7974fc64006d0b87c445cd189c2e2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 22 Dec 2021 16:39:10 +0100 Subject: [PATCH 164/898] refactor: don't coalesce netpol egress rules --- jupyterhub/templates/_helpers-netpol.tpl | 35 ++++++++---------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 6d8659f362..3778e51419 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -3,10 +3,9 @@ common configuration. It is rendering based on the `egressAllowRules` and `egress` keys of the - passed networkPolicy config object. In practice each flag under - egressAllowRules could be a dedicated egress rule, but for potential - performance impact we have increased the complexity of this rendering logic - and render them in a coalesced form. + passed networkPolicy config object. Each flag set to true under + `egressAllowRules` is rendered to a egress rule that next to any custom user + defined rules from the `egress` config. This named template needs to render based on a specific networkPolicy resource, but also needs access to the root context. Due to that, it @@ -28,7 +27,7 @@ {{- define "jupyterhub.networkPolicy.renderEgressRules" -}} {{- $root := index . 0 }} {{- $netpol := index . 1 }} -{{- if and ($netpol.egressAllowRules.dnsPortsPrivateIPs) (not $netpol.egressAllowRules.privateIPs) }} +{{- if $netpol.egressAllowRules.dnsPortsPrivateIPs }} # Allow outbound connections to the DNS port in the private IP ranges - ports: - protocol: UDP @@ -46,33 +45,21 @@ {{- if $netpol.egressAllowRules.nonPrivateIPs }} # Allow outbound connections to non-private IP ranges -{{- if $netpol.egressAllowRules.privateIPs }} -# Allow outbound connections to private IP ranges (and their DNS ports) -{{- end }} -{{- if $netpol.egressAllowRules.cloudMetadataServer }} -# Allow outbound connections to cloud metadata server -{{- end }} - to: - ipBlock: cidr: 0.0.0.0/0 - {{- if or (not $netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.cloudMetadataServer) }} except: - {{- if not $netpol.egressAllowRules.privateIPs }} - # Don't allow outbound connections to private IP ranges + # As part of this rule, don't: + # - allow outbound connections to private IP - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 - {{- end }} - - {{- if not $netpol.egressAllowRules.cloudMetadataServer }} - # Don't allow access to the cloud metadata server + # - allow outbound connections to the cloud metadata server - {{ $root.Values.singleuser.cloudMetadata.ip }}/32 - {{- end }} - {{- end }} {{- end }} -{{- if and ($netpol.egressAllowRules.privateIPs) (not $netpol.egressAllowRules.nonPrivateIPs) }} -# Allow outbound connections to private IP ranges (and their DNS ports) +{{- if $netpol.egressAllowRules.privateIPs }} +# Allow outbound connections to private IP ranges - to: - ipBlock: cidr: 10.0.0.0/8 @@ -82,8 +69,8 @@ cidr: 192.168.0.0/16 {{- end }} -{{- if and ($netpol.egressAllowRules.cloudMetadataServer) (not $netpol.egressAllowRules.nonPrivateIPs) }} -# Allow outbound connections to cloud metadata server +{{- if $netpol.egressAllowRules.cloudMetadataServer }} +# Allow outbound connections to the cloud metadata server - to: - ipBlock: cidr: {{ $root.Values.singleuser.cloudMetadata.ip }}/32 From af9f837c0e6a91b67aa9394c828d5836f993496e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 19 Jan 2022 03:23:05 +0100 Subject: [PATCH 165/898] ci: test against k8s 1.23 --- .github/workflows/test-chart.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index e91c64daee..a780144fd7 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -124,9 +124,11 @@ jobs: # k3s-version: https://github.com/rancher/k3s/tags # k3s-channel: https://update.k3s.io/v1-release/channels include: - - k3s-channel: v1.22 + - k3s-channel: v1.23 test: install debuggable: debuggable + - k3s-channel: v1.22 + test: install - k3s-channel: v1.21 test: install - k3s-channel: v1.20 From 7ffa01a9159eb1f5a2348cfe9fb22839867a00d2 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 20 Jan 2022 01:19:01 +0000 Subject: [PATCH 166/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index ce8b054e2b..2d862af0fc 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-01-14_01:13:15 +# VULN_SCAN_TIME=2022-01-20_01:19:00 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From bbe859d4abe8fddab5bea3ab74f6a4016b489383 Mon Sep 17 00:00:00 2001 From: Ostap Konstantinov Date: Tue, 25 Jan 2022 12:53:05 +1000 Subject: [PATCH 167/898] Fix converting nodeSelector to Yaml --- jupyterhub/templates/hub/deployment.yaml | 5 ++++- jupyterhub/templates/image-puller/_helpers-daemonset.tpl | 5 ++++- jupyterhub/templates/image-puller/job.yaml | 5 ++++- jupyterhub/templates/proxy/autohttps/deployment.yaml | 5 ++++- jupyterhub/templates/proxy/deployment.yaml | 5 ++++- .../templates/scheduling/user-placeholder/statefulset.yaml | 5 ++++- .../templates/scheduling/user-scheduler/deployment.yaml | 5 ++++- 7 files changed, 28 insertions(+), 7 deletions(-) diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index 2068442786..2060b97732 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -33,7 +33,10 @@ spec: {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.hub.nodeSelector }} + {{- with .Values.hub.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.corePods.tolerations .Values.hub.tolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index 6de33b85bc..434ac6300f 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -50,7 +50,10 @@ spec: {{- if and (not .hook) .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.singleuser.nodeSelector }} + {{- with .Values.singleuser.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations .Values.prePuller.extraTolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml index a6a2bf0487..dff648c6e8 100644 --- a/jupyterhub/templates/image-puller/job.yaml +++ b/jupyterhub/templates/image-puller/job.yaml @@ -37,7 +37,10 @@ spec: {{- if .Values.rbac.enabled }} serviceAccountName: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.prePuller.hook.nodeSelector }} + {{- with .Values.prePuller.hook.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.corePods.tolerations .Values.prePuller.hook.tolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index f6b7a6af97..7b55faed18 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -33,7 +33,10 @@ spec: {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.proxy.traefik.nodeSelector }} + {{- with .Values.proxy.traefik.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.traefik.tolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml index d59abd42df..9f58f945b4 100644 --- a/jupyterhub/templates/proxy/deployment.yaml +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -44,7 +44,10 @@ spec: {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.proxy.chp.nodeSelector }} + {{- with .Values.proxy.chp.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.chp.tolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index d92bb05c6e..9bc7150255 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -33,7 +33,10 @@ spec: {{- if .Values.scheduling.userScheduler.enabled }} schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.singleuser.nodeSelector }} + {{- with .Values.singleuser.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations }} tolerations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index e8da8b5991..b5ae897e15 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -23,7 +23,10 @@ spec: {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} {{- end }} - nodeSelector: {{ toJson .Values.scheduling.userScheduler.nodeSelector }} + {{- with .Values.scheduling.userScheduler.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with concat .Values.scheduling.corePods.tolerations .Values.scheduling.userScheduler.tolerations }} tolerations: {{- . | toYaml | nindent 8 }} From 30f2255bd2d99569bf3110608ceef87ec10c1ffe Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 26 Jan 2022 00:29:13 +0100 Subject: [PATCH 168/898] bump JupyterHub from 2.0.2 to 2.1.1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 78df8828ef..270ed2dce0 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.0.2 +jupyterhub==2.1.1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 6f0b259472..329926c823 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,7 +61,7 @@ jsonschema==3.2.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.0.2 +jupyterhub==2.1.1 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 9108081cec..fc4bccc833 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.0.2 +jupyterhub==2.1.1 nbgitpuller==1.0.2 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 3dc871cf87..7f9f83309c 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.0.2 +appVersion: 2.1.1 description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From e9eb04668627164074e1838c4a732357bb974ea3 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Wed, 26 Jan 2022 01:13:55 +0000 Subject: [PATCH 169/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 702384804c..0da326faab 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-01-14_01:12:28 +# VULN_SCAN_TIME=2022-01-26_01:13:53 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 773264a9a713fdd8b84de2e461a4409ecc09244a Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sun, 30 Jan 2022 01:07:02 +0000 Subject: [PATCH 170/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index d21048699e..954a0f28d9 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2021-11-14_01:05:25 +# VULN_SCAN_TIME=2022-01-30_01:07:01 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 409ee3f8a0bfd627b16b791b4124615b0fb5acd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 05:09:07 +0000 Subject: [PATCH 171/898] build(deps): bump peter-evans/create-pull-request from 3.12.0 to 3.12.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.12.0 to 3.12.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/dcd5fd746d53dd8de555c0f10bca6c35628be47a...f22a7da129c901513876a2380e2dae9f8e145330) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index d218921baf..4f358f754c 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@dcd5fd746d53dd8de555c0f10bca6c35628be47a + uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From dbbe0de17439b98b8684625d37e2afbe32e6b3e3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Jan 2022 22:16:55 +0000 Subject: [PATCH 172/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.12b0 → 22.1.0](https://github.com/psf/black/compare/21.12b0...22.1.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index db5069386d..80e0f334d7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 21.12b0 + rev: 22.1.0 hooks: - id: black args: [--target-version=py36] From cf825a3a09407f2eb3b49ae27c2dfa7662c2537c Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Feb 2022 18:00:16 +0200 Subject: [PATCH 173/898] Corrected datascience to Data Science. --- docs/source/jupyterhub/customizing/user-environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 0fea082bd5..13b882b596 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -31,7 +31,7 @@ to choose a suitable image](https://jupyter-docker-stacks.readthedocs.io/en/late If you wish to use another image from jupyter/docker-stacks than the [base-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-base-notebook) used by default, such as the [datascience-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook) -image containing useful tools and libraries for datascience, complete these steps: +image containing useful tools and libraries for Data Science, complete these steps: 1. Modify your `config.yaml` file to specify the image. For example: From 4010152ac8ebfb2e093eb67fa96dc6488705b57f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 2 Feb 2022 02:18:22 +0100 Subject: [PATCH 174/898] Apply suggestions from code review --- docs/source/jupyterhub/customizing/user-environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 13b882b596..84ef841f7d 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -31,7 +31,7 @@ to choose a suitable image](https://jupyter-docker-stacks.readthedocs.io/en/late If you wish to use another image from jupyter/docker-stacks than the [base-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-base-notebook) used by default, such as the [datascience-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook) -image containing useful tools and libraries for Data Science, complete these steps: +image containing useful tools and libraries for data science, complete these steps: 1. Modify your `config.yaml` file to specify the image. For example: From 6ef0fef92b10ae18ca1626ce65d74f9a588b7368 Mon Sep 17 00:00:00 2001 From: Nicholas Reith Date: Wed, 2 Feb 2022 20:24:15 -0600 Subject: [PATCH 175/898] Update to golang:1.17 base image to patch 1 critical and 5 high vulnerabilities --- images/image-awaiter/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/image-awaiter/Dockerfile b/images/image-awaiter/Dockerfile index 8eeac1f8e9..5740fc05cb 100644 --- a/images/image-awaiter/Dockerfile +++ b/images/image-awaiter/Dockerfile @@ -1,5 +1,5 @@ # compile the code to an executable using an intermediary image -FROM golang:1.15 +FROM golang:1.17 # VULN_SCAN_TIME= From a2281242417e4b6a69e5acbad96cdb0b6440a5b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Feb 2022 05:06:54 +0000 Subject: [PATCH 176/898] build(deps): bump aquasecurity/trivy-action from 0.2.1 to 0.2.2 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.1 to 0.2.2. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/8f4c7160b470bafe4299efdc1c8a1fb495f8325a...a7a829a4345428ddd92ca57b18257440f6a18c90) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 4f358f754c..23d16ac932 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a + uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a + uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8f4c7160b470bafe4299efdc1c8a1fb495f8325a + uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 with: image-ref: rebuilt-image format: table From 3ca59b1e04ea09437fa55b1af9b271c29a54eb3d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 3 Feb 2022 11:10:04 +0100 Subject: [PATCH 177/898] bump chp from 4.5.0 to 4.5.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c1daf020ab..9f99e972ca 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -194,7 +194,7 @@ proxy: allowPrivilegeEscalation: false image: name: jupyterhub/configurable-http-proxy - tag: 4.5.0 # https://github.com/jupyterhub/configurable-http-proxy/releases + tag: 4.5.1 # https://github.com/jupyterhub/configurable-http-proxy/releases pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From ae46bdd17bbd8c3b8242ae0f243ffb03d7b8011c Mon Sep 17 00:00:00 2001 From: Tarashish Mishra Date: Fri, 4 Feb 2022 20:56:43 +0530 Subject: [PATCH 178/898] docs: fix syntax error in note directive --- docs/source/jupyterhub/customizing/user-environment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 84ef841f7d..5eefc170d5 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -517,11 +517,11 @@ However, an image may have a custom CMD that does this, with some preparation steps, or adding additional command-line arguments, or launching a custom wrapper command, etc. -:::{note} +```{note} If you have environment preparation at startup in your image, this is best done in the ENTRYPOINT of the image, and not in the CMD, so that overriding the command does not skip your preparation. -::: +``` By default, zero-to-jupyterhub will launch the default CMD that is specified in your chosen image, respecting any startup customization that image may have. From a6294ddad7a52d609e72528cfd46d4c0a68e2b64 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sat, 5 Feb 2022 00:59:17 +0000 Subject: [PATCH 179/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 2d862af0fc..113bde705c 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-01-20_01:19:00 +# VULN_SCAN_TIME=2022-02-05_00:59:16 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 5748d00e45d0017e1125dac38fe1503e129b9518 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 10 Feb 2022 01:09:44 +0000 Subject: [PATCH 180/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 0da326faab..cdaef71446 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-01-26_01:13:53 +# VULN_SCAN_TIME=2022-02-10_01:09:43 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From d27e1ddec93dd4ef7591eadb6da5c467f76a8710 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 10 Feb 2022 01:10:08 +0000 Subject: [PATCH 181/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 113bde705c..67d787d2b9 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-02-05_00:59:16 +# VULN_SCAN_TIME=2022-02-10_01:10:07 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 73c4d7f9068154caf8b2e0810bac1df1ff0d58e1 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 11 Feb 2022 23:08:27 +0000 Subject: [PATCH 182/898] Support idle culler --cull-admin-users --- jupyterhub/files/hub/jupyterhub_config.py | 3 +++ jupyterhub/schema.yaml | 3 +++ jupyterhub/values.yaml | 1 + 3 files changed, 7 insertions(+) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index f7733fefa3..cd593bc01d 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -362,6 +362,9 @@ def camelCaseify(s): cull_cmd.append("--cull-users") jupyterhub_idle_culler_role["scopes"].append("admin:users") + if not get_config("cull.adminUsers"): + cull_cmd.append("--cull-admin-users=false") + if get_config("cull.removeNamedServers"): cull_cmd.append("--remove-named-servers") diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fb142d9819..40594cf934 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2611,6 +2611,9 @@ properties: users: type: [boolean, "null"] description: See the `--cull-users` flag. + adminUsers: + type: [boolean, "null"] + description: See the `--cull-admin-users` flag. removeNamedServers: type: [boolean, "null"] description: See the `--remove-named-servers` flag. diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 9f99e972ca..cdfb50fd17 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -560,6 +560,7 @@ ingress: cull: enabled: true users: false # --cull-users + adminUsers: true # --cull-admin-users removeNamedServers: false # --remove-named-servers timeout: 3600 # --timeout every: 600 # --cull-every From 6a21d06befe8086d25d7f80ed8a922d39a290456 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Feb 2022 00:06:24 +0000 Subject: [PATCH 183/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/2.0.0...2.0.1) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 329926c823..ec8ddd4758 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -77,7 +77,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==2.0.0 +jupyterhub-kubespawner==2.0.1 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -185,7 +185,7 @@ requests-oauthlib==1.3.0 # mwoauth rsa==4.7.2 # via google-auth -ruamel.yaml==0.17.16 +ruamel-yaml==0.17.16 # via jupyter-telemetry six==1.16.0 # via From a0ed6c15f5c59f5c1559fbf9f5e041e4e014fc52 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Feb 2022 19:18:53 +0100 Subject: [PATCH 184/898] ci: remove conditional tmate debugging session action --- .github/workflows/test-chart.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index a780144fd7..c8d7c60552 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -342,11 +342,3 @@ jobs: if: always() with: important-workloads: deploy/hub deploy/proxy - - # WARNING: Only allow this for pull_request runs that doesn't contain - # sensitive information. - # - # action reference: https://github.com/mxschmitt/action-tmate@v3 - - name: To enter a SSH debugging session, read these logs - if: failure() && github.event_name == 'pull_request' && matrix.debuggable == 'debuggable' - uses: mxschmitt/action-tmate@v3 From a31218b9e8add75be435cecc2736ac3daf240333 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sat, 19 Feb 2022 01:12:37 +0000 Subject: [PATCH 185/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 67d787d2b9..c7e032d370 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-02-10_01:10:07 +# VULN_SCAN_TIME=2022-02-19_01:12:36 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From b262750ce9b5bb47351f5fbd4d65132b6bf9b8bb Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 22 Feb 2022 01:13:57 +0000 Subject: [PATCH 186/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index cdaef71446..2e77b7c93e 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-02-10_01:09:43 +# VULN_SCAN_TIME=2022-02-22_01:13:56 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From ce2bcfb5ff06d8898fca50a014a032719bb655b7 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 22 Feb 2022 01:14:37 +0000 Subject: [PATCH 187/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index c7e032d370..dfc1129469 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-02-19_01:12:36 +# VULN_SCAN_TIME=2022-02-22_01:14:36 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From b93a9b5f5f982985031fcd8e3f3c5ecab39001f1 Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Thu, 24 Feb 2022 00:33:26 +0530 Subject: [PATCH 188/898] Bump version of traefik used by autohttps --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 9f99e972ca..525f89327e 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -239,7 +239,7 @@ proxy: allowPrivilegeEscalation: false image: name: traefik - tag: v2.5.6 # ref: https://hub.docker.com/_/traefik?tab=tags + tag: v2.6.1 # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 065790211abc3364733bb5e691be0814179b9017 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 24 Feb 2022 01:16:46 +0000 Subject: [PATCH 189/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 954a0f28d9..d359bd7328 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-01-30_01:07:01 +# VULN_SCAN_TIME=2022-02-24_01:16:45 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 1619dc2d41f5a291dad2c917c9957f744b0a9783 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 24 Feb 2022 01:17:59 +0000 Subject: [PATCH 190/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index dfc1129469..1efff70589 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-02-22_01:14:36 +# VULN_SCAN_TIME=2022-02-24_01:17:58 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 5e9d7de03eca0bac86957f5e206a73434b3230a4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 24 Feb 2022 10:21:37 +0100 Subject: [PATCH 191/898] Update comment in requirements.txt --- docs/requirements.txt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 713cf3685c..04a5387a83 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,12 +1,11 @@ -# Order matters: -# - myst-parser depends on "sphinx>=2,<4" -# - pydata-sphinx-theme depends on "sphinx" -# - sphinx-copybutton depends on "sphinx>=1.8" +# The ordering of these requirements can matter. It matters if we install them +# in an environment where a dependency to them (such as sphinx) is +# pre-installed, such as it can be on RTD. For example, if a top-ordered package +# declares it needs sphinx>=1 and then a later package declares it needs +# sphinx<4 - the latter may be ignored. # -# Listing either pydata-sphinx-theme or sphinx-copybutton first will make the -# myst-parser constraints on sphinx be ignored, so myst-parser should go -# first. This is only relevant if sphinx==1.* is already installed in the -# environment, which it is on ReadTheDocs. +# As long as we have myst-parser ordered before other sphinx depending packages, +# we should be good. # chartpress myst-parser From 86aeb6ad215a0fc5439187c659aaa4c61f488553 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 24 Feb 2022 10:21:53 +0100 Subject: [PATCH 192/898] docs: fix broken internal references --- docs/source/administrator/optimization.md | 4 +++- docs/source/administrator/upgrading.md | 8 +++++--- docs/source/resources/community.md | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/source/administrator/optimization.md b/docs/source/administrator/optimization.md index ee5c23b067..75a392b2e9 100644 --- a/docs/source/administrator/optimization.md +++ b/docs/source/administrator/optimization.md @@ -120,7 +120,7 @@ situations: generally fail to do so. This is because it will only add a node if one or more pods won't fit on the current nodes but would fit more if a node is added, but at that point users are already waiting. To scale up nodes ahead - of time we can use [user-placeholders](#scaling-up-in-time-user-placeholders). + of time we can use [user-placeholders](scaling-up-in-time-user-placeholders). (images-that-will-be-pulled)= @@ -179,6 +179,8 @@ Autoscaler_](https://github.com/kubernetes/autoscaler/tree/HEAD/cluster-autoscal help to function well. Without help, it will both fail to scale up before users arrive and scale down nodes aggressively enough without disrupting users. +(scaling-up-in-time-user-placeholders)= + ### Scaling up in time (user placeholders) A _Cluster Autoscaler_ (CA) will add nodes when pods don't fit on available diff --git a/docs/source/administrator/upgrading.md b/docs/source/administrator/upgrading.md index d29be05054..ccd88b2fe7 100644 --- a/docs/source/administrator/upgrading.md +++ b/docs/source/administrator/upgrading.md @@ -48,6 +48,8 @@ This section covers upgrade information specific to the following: - RBAC (Role Based Access Control) - Custom Docker images +(helm-upgrade-command)= + ### `helm upgrade` command After modifying your `config.yaml` file according to the CHANGELOG, you will need @@ -79,7 +81,7 @@ you are using the default database provider (SQLite), then the required db upgra will be performed automatically when you do a `helm upgrade`. **Default (SQLite)**: The database upgrade will be performed automatically when you -[perform the upgrade](#upgrade-command) +[perform the upgrade](helm-upgrade-command) **MySQL / PostgreSQL**: You will execute the following steps, which includes a manual update of your database: @@ -94,8 +96,8 @@ will be performed automatically when you do a `helm upgrade`. upgrade: true ``` -4. Do a [`helm upgrade`](#upgrade-command). This should perform the database upgrade needed. -5. Remove the lines added in step 3, and do another [`helm upgrade`](#upgrade-command). +4. Do a [`helm upgrade`](helm-upgrade-command). This should perform the database upgrade needed. +5. Remove the lines added in step 3, and do another [`helm upgrade`](helm-upgrade-command). ### Custom Docker Images: JupyterHub version match diff --git a/docs/source/resources/community.md b/docs/source/resources/community.md index af3838ee36..04f2fecba3 100644 --- a/docs/source/resources/community.md +++ b/docs/source/resources/community.md @@ -19,7 +19,7 @@ JupyterHub guide, please see the [issues page](https://github.com/jupyterhub/zer We hope that you will use this section to share deployments with on a variety of infrastructure and for different use cases. -There is also a [community maintained list](#users-list) of users of this +There is also a [community maintained list](users-list) of users of this Guide and the JupyterHub Helm Chart. Please submit an [issue/pull request](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues) to add to this section. Thanks. From cd9cebe3511b05e2305d8d217abdf9ba5f62a625 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Feb 2022 13:05:56 +0100 Subject: [PATCH 193/898] ci: remove outdated debuggable option --- .github/workflows/test-chart.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index c8d7c60552..3b24143bd5 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -126,7 +126,6 @@ jobs: include: - k3s-channel: v1.23 test: install - debuggable: debuggable - k3s-channel: v1.22 test: install - k3s-channel: v1.21 From a40eb9439942e16829069a57602b22b9bdab18b9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Feb 2022 13:14:25 +0100 Subject: [PATCH 194/898] Remove no longer relevant comment --- .../templates/scheduling/user-scheduler/deployment.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index b5ae897e15..97b19a1ca9 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -47,13 +47,6 @@ spec: {{- end }} command: - /usr/local/bin/kube-scheduler - # NOTE: --leader-elect-... (new) and --lock-object-... (deprecated) - # flags are silently ignored in favor of whats defined in the - # passed KubeSchedulerConfiguration whenever --config is - # passed. - # - # ref: https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/ - # # NOTE: --authentication-skip-lookup=true is used to avoid a # seemingly harmless error, if we need to not skip # "authentication lookup" in the future, see the linked issue. From 5e03832ff474ff8b9a38bfd6458801172b698180 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Feb 2022 13:14:53 +0100 Subject: [PATCH 195/898] user-scheduler: switch to HTTPS based liveness/readiness probes In modern versions of kube-scheduler, the HTTP based endpoint is removed. By using the HTTPS based endpoint, we won't have issues. --- .../templates/scheduling/user-scheduler/deployment.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 97b19a1ca9..da43ff3aff 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -61,12 +61,14 @@ spec: livenessProbe: httpGet: path: /healthz - port: 10251 + scheme: HTTPS + port: 10259 initialDelaySeconds: 15 readinessProbe: httpGet: path: /healthz - port: 10251 + scheme: HTTPS + port: 10259 {{- with .Values.scheduling.userScheduler.resources }} resources: {{- . | toYaml | nindent 12 }} From 2bceedeb5d337491cf6c74617536b56b011e5fb6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Feb 2022 13:17:04 +0100 Subject: [PATCH 196/898] user-scheduler: add rbac permissions required by kube-scheduler 1.21+ --- .../scheduling/user-scheduler/rbac.yaml | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 0056f3525c..cc6b263a3e 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -19,13 +19,20 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} rules: # Copied from the system:kube-scheduler ClusterRole of the k8s version - # matching the kube-scheduler binary we use. A modification of two resource - # name references from kube-scheduler to user-scheduler-lock was made. + # matching the kube-scheduler binary we use. A modification has been made to + # resourceName fields to remain relevant for how we have named our resources + # in this Helm chart. # - # NOTE: These rules have been unchanged between 1.12 and 1.15, then changed in - # 1.16 and in 1.17, but unchanged in 1.18 and 1.19. + # NOTE: These rules have been: + # - unchanged between 1.12 and 1.15 + # - changed in 1.16 + # - changed in 1.17 + # - unchanged between 1.18 and 1.20 + # - changed in 1.21: get/list/watch permission for namespace, + # csidrivers, csistoragecapacities was added. + # - unchanged between 1.22 and 1.23 # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.19.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L696-L829 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L705-L861 - apiGroups: - "" - events.k8s.io @@ -159,13 +166,37 @@ rules: - get - list - watch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - csidrivers + verbs: + - get + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - csistoragecapacities + verbs: + - get + - list + - watch # Copied from the system:volume-scheduler ClusterRole of the k8s version # matching the kube-scheduler binary we use. # - # NOTE: These rules have not changed between 1.12 and 1.19. + # NOTE: These rules have not changed between 1.12 and 1.23. # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.19.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1213-L1240 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1280-L1307 - apiGroups: - "" resources: From cc7e7288ea3ccc379988d2c8e816517866931d9a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Feb 2022 18:53:37 +0100 Subject: [PATCH 197/898] user-scheduler: use modern kube-scheduler binary and config --- jupyterhub/schema.yaml | 6 +- .../scheduling/user-scheduler/configmap.yaml | 71 ++++++++++++++++- .../scheduling/user-scheduler/deployment.yaml | 10 +++ jupyterhub/values.yaml | 76 ++++++++++++++++--- tools/templates/lint-and-validate-values.yaml | 8 ++ 5 files changed, 158 insertions(+), 13 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fb142d9819..81165ecaf6 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2162,7 +2162,7 @@ properties: userScheduler: type: object additionalProperties: false - required: [enabled, plugins, logLevel] + required: [enabled, plugins, pluginConfig, logLevel] description: | The user scheduler is making sure that user pods are scheduled tight on nodes, this is useful for autoscaling of user node pools. @@ -2219,6 +2219,10 @@ properties: type: string weight: type: integer + pluginConfig: + type: array + description: | + Individually activated plugins can be configured further. resources: *resources-spec serviceAccount: *serviceAccount extraPodSpec: *extraPodSpec-spec diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml index 60d533967b..b141806add 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -6,8 +6,51 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} data: - # ref: https://kubernetes.io/docs/reference/scheduling/config/ + {{- /* + This is configuration of a k8s official kube-scheduler binary running in the + user-scheduler pod. + + ref: https://kubernetes.io/docs/reference/scheduling/config/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta2/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ + + v1beta1 can be used with kube-scheduler binary version <=1.21 + v1beta2 requires kube-scheduler binary version >=1.22 + v1beta3 requires kube-scheduler binary version >=1.23 + + kube-scheduler binaries versioned >=1.21 will error in k8s clusters + versioned <=1.20. To support a modern version of kube-scheduler and k8s + versions <=1.20 upwards, we provide two scenarios: + + 1. For k8s >= 1.21 we use a modern version of kube-scheduler and Helm chart + configuration works as expected. + 2. For k8s <= 1.20 we use a hardcoded version of kube-scheduler (v1.20.15) + and configuration (v1beta1) of kube-scheduler. + */}} config.yaml: | + {{- if ge (atoi .Capabilities.KubeVersion.Minor) 21 }} + apiVersion: kubescheduler.config.k8s.io/v1beta3 + kind: KubeSchedulerConfiguration + leaderElection: + resourceLock: endpoints + resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} + resourceNamespace: "{{ .Release.Namespace }}" + profiles: + - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} + {{- with .Values.scheduling.userScheduler.plugins }} + plugins: + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- with .Values.scheduling.userScheduler.pluginConfig }} + pluginConfig: + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- else }} + # WARNING: The tag of this image is hardcoded, and the + # "scheduling.userScheduler.plugins" configuration of the Helm + # chart that generated this resource manifest wasn't respected. If + # you install the Helm chart in a k8s cluster versioned 1.21 or + # higher, your configuration will be respected. apiVersion: kubescheduler.config.k8s.io/v1beta1 kind: KubeSchedulerConfiguration leaderElection: @@ -17,5 +60,29 @@ data: profiles: - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} plugins: - {{- .Values.scheduling.userScheduler.plugins | toYaml | nindent 10 }} + score: + disabled: + - name: SelectorSpread + - name: TaintToleration + - name: PodTopologySpread + - name: NodeResourcesBalancedAllocation + - name: NodeResourcesLeastAllocated + # Disable plugins to be allowed to enable them again with a + # different weight and avoid an error. + - name: NodePreferAvoidPods + - name: NodeAffinity + - name: InterPodAffinity + - name: ImageLocality + enabled: + - name: NodePreferAvoidPods + weight: 161051 + - name: NodeAffinity + weight: 14631 + - name: InterPodAffinity + weight: 1331 + - name: NodeResourcesMostAllocated + weight: 121 + - name: ImageLocality + weight: 11 + {{- end }} {{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index da43ff3aff..efbd46661f 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -41,7 +41,17 @@ spec: {{- end }} containers: - name: kube-scheduler + {{- if ge (atoi .Capabilities.KubeVersion.Minor) 21 }} image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} + {{- else }} + # WARNING: The tag of this image is hardcoded, and the + # "scheduling.userScheduler.image.tag" configuration of the + # Helm chart that generated this resource manifest wasn't + # respected. If you install the Helm chart in a k8s cluster + # versioned 1.21 or higher, your configuration will be + # respected. + image: {{ .Values.scheduling.userScheduler.image.name }}:v1.20.15 + {{- end }} {{- with .Values.scheduling.userScheduler.image.pullPolicy }} imagePullPolicy: {{ . }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 525f89327e..1a6828d27b 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -398,32 +398,63 @@ scheduling: enabled: true replicas: 2 logLevel: 4 + # plugins are configured on the user-scheduler to make us score how we + # schedule user pods in a way to help us schedule on the most busy node. By + # doing this, we help scale down more effectively. It isn't obvious how to + # enable/disable scoring plugins, and configure them, to accomplish this. + # # plugins ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins-1 + # migration ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduler-configuration-migrations + # plugins: score: + # These scoring plugins are enabled by default according to + # https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins + # 2022-02-22. + # + # Enabled with high priority: + # - NodeAffinity + # - InterPodAffinity + # - NodeResourcesFit + # - ImageLocality + # Remains enabled with low default priority: + # - TaintToleration + # - PodTopologySpread + # - VolumeBinding + # Disabled for scoring: + # - NodeResourcesBalancedAllocation + # disabled: - - name: SelectorSpread - - name: TaintToleration - - name: PodTopologySpread + # We disable these plugins (with regards to scoring) to not interfere + # or complicate our use of NodeResourcesFit. - name: NodeResourcesBalancedAllocation - - name: NodeResourcesLeastAllocated # Disable plugins to be allowed to enable them again with a different # weight and avoid an error. - - name: NodePreferAvoidPods - name: NodeAffinity - name: InterPodAffinity + - name: NodeResourcesFit - name: ImageLocality enabled: - - name: NodePreferAvoidPods - weight: 161051 - name: NodeAffinity weight: 14631 - name: InterPodAffinity weight: 1331 - - name: NodeResourcesMostAllocated + - name: NodeResourcesFit weight: 121 - name: ImageLocality weight: 11 + pluginConfig: + # Here we declare that we should optimize pods to fit based on a + # MostAllocated strategy instead of the default LeastAllocated. + - name: NodeResourcesFit + args: + scoringStrategy: + resources: + - name: cpu + weight: 1 + - name: memory + weight: 1 + type: MostAllocated containerSecurityContext: runAsUser: 65534 # nobody user runAsGroup: 65534 # nobody group @@ -431,9 +462,34 @@ scheduling: image: # IMPORTANT: Bumping the minor version of this binary should go hand in # hand with an inspection of the user-scheduelrs RBAC resources - # that we have forked. + # that we have forked in + # templates/scheduling/user-scheduler/rbac.yaml. + # + # Debugging advice: + # + # - Is configuration of kube-scheduler broken in + # templates/scheduling/user-scheduler/configmap.yaml? + # + # - Is the kube-scheduler binary's compatebility to work + # against a k8s api-server that is too new or too old? + # + # - You can update the GitHub workflow that runs tests to + # include "deploy/user-scheduler" in the k8s namespace report + # and reduce the user-scheduler deployments replicas to 1 in + # dev-config.yaml to get relevant logs from the user-scheduler + # pods. Inspect the "Kubernetes namespace report" action! + # + # - Typical failures are that kube-scheduler fail to search for + # resources via its "informers", and won't start trying to + # schedule pods before they succeed which may require + # additional RBAC permissions or that the k8s api-server is + # aware of the resources. + # + # - If "successfully acquired lease" can be seen in the logs, it + # is a good sign kube-scheduler is ready to schedule pods. + # name: k8s.gcr.io/kube-scheduler - tag: v1.19.16 # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: v1.23.4 # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 3183354518..7671bab64a 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -451,6 +451,14 @@ scheduling: - name: NodePreferAvoidPods weight: 161051 - name: NodeAffinity + pluginConfig: + - name: NodeResourcesFit + args: + scoringStrategy: + resources: + - name: cpu + weight: 1 + type: MostAllocated serviceAccount: *serviceAccount extraPodSpec: *extraPodSpec podPriority: From b31f87aa64f1ee6edb5e828b29b64252852a361d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 25 Feb 2022 11:46:21 +0100 Subject: [PATCH 198/898] Apply suggestions from code review Co-authored-by: Simon Li --- .../templates/scheduling/user-scheduler/deployment.yaml | 2 +- jupyterhub/values.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index efbd46661f..78406a1582 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -46,7 +46,7 @@ spec: {{- else }} # WARNING: The tag of this image is hardcoded, and the # "scheduling.userScheduler.image.tag" configuration of the - # Helm chart that generated this resource manifest wasn't + # Helm chart that generated this resource manifest isn't # respected. If you install the Helm chart in a k8s cluster # versioned 1.21 or higher, your configuration will be # respected. diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 1a6828d27b..5e7d68b399 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -470,7 +470,7 @@ scheduling: # - Is configuration of kube-scheduler broken in # templates/scheduling/user-scheduler/configmap.yaml? # - # - Is the kube-scheduler binary's compatebility to work + # - Is the kube-scheduler binary's compatibility to work # against a k8s api-server that is too new or too old? # # - You can update the GitHub workflow that runs tests to @@ -479,7 +479,7 @@ scheduling: # dev-config.yaml to get relevant logs from the user-scheduler # pods. Inspect the "Kubernetes namespace report" action! # - # - Typical failures are that kube-scheduler fail to search for + # - Typical failures are that kube-scheduler fails to search for # resources via its "informers", and won't start trying to # schedule pods before they succeed which may require # additional RBAC permissions or that the k8s api-server is From f16ee3c9b6a9e12ac7ec33cda8e256c60dd3898e Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 25 Feb 2022 21:43:50 +0000 Subject: [PATCH 199/898] Remove typo " in schema.yaml --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fb142d9819..209a283e0f 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -11,7 +11,7 @@ # # ref: https://json-schema.org/learn/getting-started-step-by-step.html # -$schema": http://json-schema.org/draft-07/schema# +$schema: http://json-schema.org/draft-07/schema# type: object additionalProperties: false required: From 2eb3160c26fc7d5e9f839cdc8ef8d26e2960b0d2 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 25 Feb 2022 21:56:41 +0000 Subject: [PATCH 200/898] Use chart logo https://jupyterhub.github.io/helm-chart/images/hublogo.svg Closes https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2599 Reverts https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2544 --- docs/source/_static/images/logo/hublogo.svg | 75 --------------------- jupyterhub/Chart.yaml | 2 +- 2 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 docs/source/_static/images/logo/hublogo.svg diff --git a/docs/source/_static/images/logo/hublogo.svg b/docs/source/_static/images/logo/hublogo.svg deleted file mode 100644 index f000235fa2..0000000000 --- a/docs/source/_static/images/logo/hublogo.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - -Group Copy 2 -Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 7f9f83309c..c8a5a928f9 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -7,7 +7,7 @@ description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] -icon: https://zero-to-jupyterhub.readthedocs.io/en/latest/_static/hublogo.svg +icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg kubeVersion: ">=1.17.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers From ab185c3ecfcaec72a7e2dae7407d707a1e8bf786 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 05:04:13 +0000 Subject: [PATCH 201/898] build(deps): bump peter-evans/create-pull-request from 3.12.1 to 3.14.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.12.1 to 3.14.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f22a7da129c901513876a2380e2dae9f8e145330...18f7dc018cc2cd597073088f7c7591b9d1c02672) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 23d16ac932..10c3b1a659 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 + uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From 908953c40364353ea7ceeae9edc1e1193317dfb1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 05:04:14 +0000 Subject: [PATCH 202/898] build(deps): bump actions/setup-python from 2 to 3 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 2 to 3. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 10 +++++----- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 02c0c4b3c9..0a46ec6277 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -40,7 +40,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 3b24143bd5..b9b6882cb6 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 - name: Install dependencies run: pip install pre-commit @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" @@ -74,7 +74,7 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" @@ -206,9 +206,9 @@ jobs: traefik-enabled: false docker-enabled: true - # NOTE: actions/setup-python@v2 make use of a cache within the GitHub base + # NOTE: actions/setup-python@v3 make use of a cache within the GitHub base # environment and setup in a fraction of a second. - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 7b39a1bf81..6d6be8f943 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -35,7 +35,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index c0cf86ebf2..e000b9c153 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -30,7 +30,7 @@ jobs: # chartpress, used by docs/conf.py, requires git history to set # chart version and image tags correctly fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 23d16ac932..859b2d6339 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -49,7 +49,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v3 with: python-version: "3.8" From e7542fd28b425e289549600877d11863ddf22c3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Mar 2022 05:06:19 +0000 Subject: [PATCH 203/898] build(deps): bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 8 ++++---- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0a46ec6277..627ce50c54 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,7 +34,7 @@ jobs: publish: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index b9b6882cb6..89515d023c 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -28,7 +28,7 @@ jobs: lint_shell_scripts: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v3 - name: Install dependencies @@ -40,7 +40,7 @@ jobs: lint_and_validate_rendered_templates: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: python-version: "3.8" @@ -73,7 +73,7 @@ jobs: - helm-version: v3.5.0 # minimal required version steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: python-version: "3.8" @@ -189,7 +189,7 @@ jobs: --set singleuser.storage.type=dynamic steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 6d6be8f943..9bbbee20ae 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -29,7 +29,7 @@ jobs: build_images: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index e000b9c153..4f5753b147 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -25,7 +25,7 @@ jobs: linkcheck: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # chartpress, used by docs/conf.py, requires git history to set # chart version and image tags correctly diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 97299fc7a2..c2db46a726 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -43,7 +43,7 @@ jobs: accept_failure: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: # chartpress requires git history to set chart version and image tags # correctly From 849ece98119e041b1f55e6458139e0f109102c3e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Mar 2022 05:05:34 +0000 Subject: [PATCH 204/898] build(deps): bump jupyterhub in /images/singleuser-sample Bumps [jupyterhub](https://github.com/jupyterhub/jupyterhub) from 2.1.1 to 2.2.0. - [Release notes](https://github.com/jupyterhub/jupyterhub/releases) - [Changelog](https://github.com/jupyterhub/jupyterhub/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/jupyterhub/compare/2.1.1...2.2.0) --- updated-dependencies: - dependency-name: jupyterhub dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index fc4bccc833..94a17fcb05 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.1.1 +jupyterhub==2.2.0 nbgitpuller==1.0.2 From 64082e3785f2864e89406c27ea157a5c75aaa706 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Mar 2022 12:26:26 +0100 Subject: [PATCH 205/898] hub image: bump jupyterhub to 2.2.0 and refreeze dependencies --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 123 +++++++++++++++++++----------------- jupyterhub/Chart.yaml | 2 +- 3 files changed, 68 insertions(+), 59 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 270ed2dce0..4562689654 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.1.1 +jupyterhub==2.2.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index ec8ddd4758..080abbb0b0 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,40 +4,40 @@ # # ./dependencies freeze --upgrade # -alembic==1.7.3 +alembic==1.7.6 # via jupyterhub async-generator==1.10 # via # jupyterhub # jupyterhub-kubespawner -attrs==21.2.0 +attrs==21.4.0 # via jsonschema bcrypt==3.2.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -cachetools==4.2.2 +cachetools==5.0.0 # via google-auth -certifi==2021.5.30 +certifi==2021.10.8 # via # kubernetes # requests certipy==0.1.3 # via jupyterhub -cffi==1.14.6 +cffi==1.15.0 # via # bcrypt # cryptography -charset-normalizer==2.0.6 +charset-normalizer==2.0.12 # via requests -cryptography==3.4.8 +cryptography==36.0.1 # via # josepy # jwcrypto # pyopenssl deprecated==1.2.13 # via jwcrypto -entrypoints==0.3 +entrypoints==0.4 # via jupyterhub escapism==1.0.1 # via @@ -45,32 +45,28 @@ escapism==1.0.1 # jupyterhub-ltiauthenticator future==0.18.2 # via pyjwkest -google-auth==2.1.0 +google-auth==2.6.0 # via kubernetes -greenlet==1.1.1 +greenlet==1.1.2 # via sqlalchemy -idna==3.2 +idna==3.3 # via requests -jinja2==3.0.1 +importlib-metadata==4.11.2 + # via alembic +importlib-resources==5.4.0 + # via + # alembic + # jsonschema +jinja2==3.0.3 # via # jupyterhub # jupyterhub-kubespawner -josepy==1.10.0 +josepy==1.12.0 # via jupyterhub-ltiauthenticator -jsonschema==3.2.0 +jsonschema==4.4.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.1.1 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator jupyterhub-firstuseauthenticator==1.0.0 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -87,15 +83,25 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in +jupyterhub==2.2.0 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator jwcrypto==1.0 # via jupyterhub-ltiauthenticator -kubernetes==18.20.0 +kubernetes==23.3.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.1.5 +mako==1.1.6 # via alembic -markupsafe==2.0.1 +markupsafe==2.1.0 # via # jinja2 # mako @@ -107,7 +113,7 @@ oauthenticator==14.2.0 # via # -r requirements.in # jupyterhub-ltiauthenticator -oauthlib==3.1.1 +oauthlib==3.2.0 # via # jupyterhub # jupyterhub-ltiauthenticator @@ -115,32 +121,32 @@ oauthlib==3.1.1 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator -packaging==21.0 +packaging==21.3 # via jupyterhub pamela==1.0.0 # via jupyterhub pem==21.2.0 # via jupyterhub-ltiauthenticator -prometheus-client==0.11.0 +prometheus-client==0.13.1 # via jupyterhub psycopg2-binary==2.9.3 # via -r requirements.in py-spy==0.3.11 # via -r requirements.in +pyasn1-modules==0.2.8 + # via google-auth pyasn1==0.4.8 # via # ldap3 # pyasn1-modules # rsa -pyasn1-modules==0.2.8 - # via google-auth -pycparser==2.20 +pycparser==2.21 # via cffi -pycryptodome==3.11.0 +pycryptodome==3.14.1 # via jupyterhub-ltiauthenticator -pycryptodomex==3.11.0 +pycryptodomex==3.14.1 # via pyjwkest -pycurl==7.44.1 +pycurl==7.45.0 # via -r requirements.in pyjwkest==1.4.2 # via jupyterhub-ltiauthenticator @@ -151,13 +157,13 @@ pyjwt==1.7.1 # mwoauth pymysql==1.0.2 # via -r requirements.in -pyopenssl==20.0.1 +pyopenssl==22.0.0 # via # certipy # josepy -pyparsing==2.4.7 +pyparsing==3.0.7 # via packaging -pyrsistent==0.18.0 +pyrsistent==0.18.1 # via jsonschema python-dateutil==2.8.2 # via @@ -166,44 +172,43 @@ python-dateutil==2.8.2 # kubernetes python-json-logger==2.0.2 # via jupyter-telemetry -python-slugify==5.0.2 +python-slugify==6.1.1 # via jupyterhub-kubespawner -pyyaml==5.4.1 +pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes -requests==2.26.0 +requests-oauthlib==1.3.1 # via - # jupyterhub # kubernetes # mwoauth - # pyjwkest - # requests-oauthlib -requests-oauthlib==1.3.0 +requests==2.27.1 # via + # jupyterhub # kubernetes # mwoauth -rsa==4.7.2 + # pyjwkest + # requests-oauthlib +rsa==4.8 # via google-auth -ruamel-yaml==0.17.16 +ruamel.yaml==0.17.21 # via jupyter-telemetry six==1.16.0 # via # bcrypt - # jsonschema + # google-auth # kubernetes # mwoauth # onetimepass # pyjwkest - # pyopenssl # python-dateutil -sqlalchemy==1.4.23 +sqlalchemy-cockroachdb==1.4.3 + # via -r requirements.in +sqlalchemy==1.4.32 # via # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.3 - # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -213,20 +218,24 @@ tornado==6.1 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.1.0 +traitlets==5.1.1 # via # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.6 +urllib3==1.26.8 # via # jupyterhub-kubespawner # kubernetes # requests -websocket-client==1.2.1 +websocket-client==1.3.1 # via kubernetes -wrapt==1.13.3 +wrapt==1.14.0 # via deprecated +zipp==3.7.0 + # via + # importlib-metadata + # importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index c8a5a928f9..8e498436d1 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: 2.1.1 +appVersion: "2.2.0" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From af55d884c9a2fc27da008ef11a0e3a19cb440535 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 11 Mar 2022 01:24:02 +0000 Subject: [PATCH 206/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 2e77b7c93e..0c4093443e 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-02-22_01:13:56 +# VULN_SCAN_TIME=2022-03-11_01:24:01 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 751bc3133fe494d2344f505e7b01e1ce0d680f17 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 11 Mar 2022 01:25:26 +0000 Subject: [PATCH 207/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 1efff70589..a28cf2aae7 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-02-24_01:17:58 +# VULN_SCAN_TIME=2022-03-11_01:25:24 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From c45160338ddad5f8dbfd17c686a523524db1eacd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 00:19:14 +0000 Subject: [PATCH 208/898] build(deps): bump pycurl from 7.45.0 to 7.45.1 in /images/hub Bumps [pycurl](https://github.com/pycurl/pycurl) from 7.45.0 to 7.45.1. - [Release notes](https://github.com/pycurl/pycurl/releases) - [Changelog](https://github.com/pycurl/pycurl/blob/master/ChangeLog) - [Commits](https://github.com/pycurl/pycurl/commits) --- updated-dependencies: - dependency-name: pycurl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 52 ++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 080abbb0b0..5d2183f800 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -51,12 +51,6 @@ greenlet==1.1.2 # via sqlalchemy idna==3.3 # via requests -importlib-metadata==4.11.2 - # via alembic -importlib-resources==5.4.0 - # via - # alembic - # jsonschema jinja2==3.0.3 # via # jupyterhub @@ -67,6 +61,16 @@ jsonschema==4.4.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub +jupyterhub==2.2.0 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator jupyterhub-firstuseauthenticator==1.0.0 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -83,16 +87,6 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jupyterhub==2.2.0 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator jwcrypto==1.0 # via jupyterhub-ltiauthenticator kubernetes==23.3.0 @@ -133,20 +127,20 @@ psycopg2-binary==2.9.3 # via -r requirements.in py-spy==0.3.11 # via -r requirements.in -pyasn1-modules==0.2.8 - # via google-auth pyasn1==0.4.8 # via # ldap3 # pyasn1-modules # rsa +pyasn1-modules==0.2.8 + # via google-auth pycparser==2.21 # via cffi pycryptodome==3.14.1 # via jupyterhub-ltiauthenticator pycryptodomex==3.14.1 # via pyjwkest -pycurl==7.45.0 +pycurl==7.45.1 # via -r requirements.in pyjwkest==1.4.2 # via jupyterhub-ltiauthenticator @@ -178,10 +172,6 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes -requests-oauthlib==1.3.1 - # via - # kubernetes - # mwoauth requests==2.27.1 # via # jupyterhub @@ -189,10 +179,16 @@ requests==2.27.1 # mwoauth # pyjwkest # requests-oauthlib +requests-oauthlib==1.3.1 + # via + # kubernetes + # mwoauth rsa==4.8 # via google-auth -ruamel.yaml==0.17.21 +ruamel-yaml==0.17.21 # via jupyter-telemetry +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml six==1.16.0 # via # bcrypt @@ -202,13 +198,13 @@ six==1.16.0 # onetimepass # pyjwkest # python-dateutil -sqlalchemy-cockroachdb==1.4.3 - # via -r requirements.in sqlalchemy==1.4.32 # via # alembic # jupyterhub # sqlalchemy-cockroachdb +sqlalchemy-cockroachdb==1.4.3 + # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -232,10 +228,6 @@ websocket-client==1.3.1 # via kubernetes wrapt==1.14.0 # via deprecated -zipp==3.7.0 - # via - # importlib-metadata - # importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools From 1c87c0d2d38ff82cc5cd32d75e57b428df99f536 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 10:02:56 +0000 Subject: [PATCH 209/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 2.0.1 to 3.0.0. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/2.0.1...3.0.0) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 67 +++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5d2183f800..72dbc0ede3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,23 +4,27 @@ # # ./dependencies freeze --upgrade # +aiohttp==3.8.1 + # via kubernetes-asyncio +aiosignal==1.2.0 + # via aiohttp alembic==1.7.6 # via jupyterhub async-generator==1.10 - # via - # jupyterhub - # jupyterhub-kubespawner + # via jupyterhub +async-timeout==4.0.2 + # via aiohttp attrs==21.4.0 - # via jsonschema + # via + # aiohttp + # jsonschema bcrypt==3.2.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -cachetools==5.0.0 - # via google-auth certifi==2021.10.8 # via - # kubernetes + # kubernetes-asyncio # requests certipy==0.1.3 # via jupyterhub @@ -29,7 +33,9 @@ cffi==1.15.0 # bcrypt # cryptography charset-normalizer==2.0.12 - # via requests + # via + # aiohttp + # requests cryptography==36.0.1 # via # josepy @@ -43,14 +49,18 @@ escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator +frozenlist==1.3.0 + # via + # aiohttp + # aiosignal future==0.18.2 # via pyjwkest -google-auth==2.6.0 - # via kubernetes greenlet==1.1.2 # via sqlalchemy idna==3.3 - # via requests + # via + # requests + # yarl jinja2==3.0.3 # via # jupyterhub @@ -77,7 +87,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==2.0.1 +jupyterhub-kubespawner==3.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -89,7 +99,7 @@ jupyterhub-tmpauthenticator==0.6 # via -r requirements.in jwcrypto==1.0 # via jupyterhub-ltiauthenticator -kubernetes==23.3.0 +kubernetes-asyncio==21.7.1 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -99,6 +109,10 @@ markupsafe==2.1.0 # via # jinja2 # mako +multidict==6.0.2 + # via + # aiohttp + # yarl mwoauth==0.3.7 # via -r requirements.in nullauthenticator==1.0.0 @@ -128,12 +142,7 @@ psycopg2-binary==2.9.3 py-spy==0.3.11 # via -r requirements.in pyasn1==0.4.8 - # via - # ldap3 - # pyasn1-modules - # rsa -pyasn1-modules==0.2.8 - # via google-auth + # via ldap3 pycparser==2.21 # via cffi pycryptodome==3.14.1 @@ -163,7 +172,7 @@ python-dateutil==2.8.2 # via # jupyterhub # jupyterhub-idle-culler - # kubernetes + # kubernetes-asyncio python-json-logger==2.0.2 # via jupyter-telemetry python-slugify==6.1.1 @@ -171,20 +180,15 @@ python-slugify==6.1.1 pyyaml==6.0 # via # jupyterhub-kubespawner - # kubernetes + # kubernetes-asyncio requests==2.27.1 # via # jupyterhub - # kubernetes # mwoauth # pyjwkest # requests-oauthlib requests-oauthlib==1.3.1 - # via - # kubernetes - # mwoauth -rsa==4.8 - # via google-auth + # via mwoauth ruamel-yaml==0.17.21 # via jupyter-telemetry ruamel-yaml-clib==0.2.6 @@ -192,8 +196,7 @@ ruamel-yaml-clib==0.2.6 six==1.16.0 # via # bcrypt - # google-auth - # kubernetes + # kubernetes-asyncio # mwoauth # onetimepass # pyjwkest @@ -222,12 +225,12 @@ traitlets==5.1.1 urllib3==1.26.8 # via # jupyterhub-kubespawner - # kubernetes + # kubernetes-asyncio # requests -websocket-client==1.3.1 - # via kubernetes wrapt==1.14.0 # via deprecated +yarl==1.7.2 + # via aiohttp # The following packages are considered to be unsafe in a requirements file: # setuptools From 09faa4ea252f5ad985c7ced46724df7b5b9d9ae5 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 23 Feb 2022 17:38:53 -0700 Subject: [PATCH 210/898] Use kubernetes_asyncio --- jupyterhub/files/hub/jupyterhub_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index f7733fefa3..9f3d01a8e6 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -6,7 +6,7 @@ from binascii import a2b_hex from tornado.httpclient import AsyncHTTPClient -from kubernetes import client +from kubernetes_asyncio import client from jupyterhub.utils import url_path_join # Make sure that modules placed in the same directory as the jupyterhub config are added to the pythonpath From 61c7e987cb64d8f2bfc01e49647da6983851a0e3 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Mon, 14 Mar 2022 15:03:00 +0000 Subject: [PATCH 211/898] Copy https://github.com/jupyterhub/jupyterhub/blob/8d056170d7184db860c4a8bf4e0bded4d4cc7b92/.github/workflows/support-bot.yml --- .github/workflows/support-bot.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/support-bot.yml diff --git a/.github/workflows/support-bot.yml b/.github/workflows/support-bot.yml new file mode 100644 index 0000000000..341d5e4105 --- /dev/null +++ b/.github/workflows/support-bot.yml @@ -0,0 +1,31 @@ +# https://github.com/dessant/support-requests +name: "Support Requests" + +on: + issues: + types: [labeled, unlabeled, reopened] + +permissions: + issues: write + +jobs: + action: + runs-on: ubuntu-latest + steps: + - uses: dessant/support-requests@v2 + with: + github-token: ${{ github.token }} + support-label: "support" + issue-comment: | + Hi there @{issue-author} :wave:! + + I closed this issue because it was labelled as a support question. + + Please help us organize discussion by posting this on the http://discourse.jupyter.org/ forum. + + Our goal is to sustain a positive experience for both users and developers. We use GitHub issues for specific discussions related to changing a repository's content, and let the forum be where we can more generally help and inspire each other. + + Thanks you for being an active member of our community! :heart: + close-issue: true + lock-issue: false + issue-lock-reason: "off-topic" From d7b09f4f868fbc4b0aa9db9dbbcb06d5765c1128 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Mon, 14 Mar 2022 15:03:36 +0000 Subject: [PATCH 212/898] Include link on how to ask questions --- .github/workflows/support-bot.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/support-bot.yml b/.github/workflows/support-bot.yml index 341d5e4105..b3828412d1 100644 --- a/.github/workflows/support-bot.yml +++ b/.github/workflows/support-bot.yml @@ -21,7 +21,9 @@ jobs: I closed this issue because it was labelled as a support question. - Please help us organize discussion by posting this on the http://discourse.jupyter.org/ forum. + Please help us organize discussion by posting this on the http://discourse.jupyter.org/ forum. If it's your first time posting + please read https://discourse.jupyter.org/t/getting-good-answers-to-your-questions/1825. + The more information you provide the more likely we can help you. Our goal is to sustain a positive experience for both users and developers. We use GitHub issues for specific discussions related to changing a repository's content, and let the forum be where we can more generally help and inspire each other. From 5e5fb4d722e7c286d8efec3d3bf57b1c55508c48 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 14 Mar 2022 19:19:07 +0100 Subject: [PATCH 213/898] Bump kubespawner to 3.0.1 to include small bugfix --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 72dbc0ede3..c62c2e7f4a 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -87,7 +87,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==3.0.0 +jupyterhub-kubespawner==3.0.1 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From 7280c1fa8f65d8471637f8ecccc08e43eb207396 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Mar 2022 23:24:35 +0000 Subject: [PATCH 214/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.31.0 → v2.31.1](https://github.com/asottile/pyupgrade/compare/v2.31.0...v2.31.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80e0f334d7..c573c7edab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.31.0 + rev: v2.31.1 hooks: - id: pyupgrade args: From 416d11b1d704fd8d9e974221b6c151935e879026 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 20:31:37 +0000 Subject: [PATCH 215/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 3.0.1 to 3.0.2. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/3.0.1...3.0.2) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index c62c2e7f4a..adf6bfdf8c 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -87,7 +87,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==3.0.1 +jupyterhub-kubespawner==3.0.2 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From 0b6b9d96ac13e393597956586cedbb1d34bd4c96 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 18 Mar 2022 01:24:04 +0000 Subject: [PATCH 216/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index d359bd7328..b95209fe8e 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-02-24_01:16:45 +# VULN_SCAN_TIME=2022-03-18_01:24:03 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 2905fa65dbab14e836ada44231c2a8d30c25c6f6 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 18 Mar 2022 01:24:16 +0000 Subject: [PATCH 217/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 7773ba1aa0..7af4530497 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2021-11-13_01:01:39 +# VULN_SCAN_TIME=2022-03-18_01:24:14 RUN apk add --no-cache iptables From b77d85e5fd63ed2f5832fda2adb3c65630e57a05 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 18 Mar 2022 01:24:57 +0000 Subject: [PATCH 218/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 0c4093443e..4a612af416 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-03-11_01:24:01 +# VULN_SCAN_TIME=2022-03-18_01:24:56 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From c980f6048af73f6a42dbb1c7eb4be0f93f116db9 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 18 Mar 2022 01:25:36 +0000 Subject: [PATCH 219/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index a28cf2aae7..9d92e04881 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-03-11_01:25:24 +# VULN_SCAN_TIME=2022-03-18_01:25:35 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From aa89506424cf176fc78a92be9b7f69497daa1ce2 Mon Sep 17 00:00:00 2001 From: jhowton-restor3d <100805929+jhowton-restor3d@users.noreply.github.com> Date: Fri, 18 Mar 2022 11:07:31 -0500 Subject: [PATCH 220/898] Set secret-sync resources in deployment template The `resources` property was not being set in the secret-sync container configuration. This is a configurable property that is described in the zero-to-jupyterhub configuration docs. --- jupyterhub/templates/proxy/autohttps/deployment.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 7b55faed18..4853c64ddb 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -118,6 +118,10 @@ spec: {{- with .Values.proxy.secretSync.image.pullPolicy }} imagePullPolicy: {{ . }} {{- end }} + {{- with .Values.proxy.secretSync.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} args: - watch-save - --label=app={{ include "jupyterhub.appLabel" . }} From 69d83d7db37cba3bb1112aa77f22143741437803 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Mar 2022 05:06:25 +0000 Subject: [PATCH 221/898] build(deps): bump nbgitpuller in /images/singleuser-sample Bumps [nbgitpuller](https://github.com/jupyterhub/nbgitpuller) from 1.0.2 to 1.1.0. - [Release notes](https://github.com/jupyterhub/nbgitpuller/releases) - [Changelog](https://github.com/jupyterhub/nbgitpuller/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/nbgitpuller/compare/1.0.2...1.1.0) --- updated-dependencies: - dependency-name: nbgitpuller dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 94a17fcb05..b47a394d04 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -2,4 +2,4 @@ # .github/dependabot.yaml. jupyterhub==2.2.0 -nbgitpuller==1.0.2 +nbgitpuller==1.1.0 From 77dea67da3ecd9e267295148dc6e37d90bb6e003 Mon Sep 17 00:00:00 2001 From: theomper <31165738+theomper@users.noreply.github.com> Date: Mon, 21 Mar 2022 14:45:37 +0200 Subject: [PATCH 222/898] update step-zero-microk8s.md Corrected a typo --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md index 6949944888..99aeb7cd3c 100644 --- a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -8,7 +8,7 @@ If you have server hardware available and a small enough user base it's possible With no ability to scale, users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. ``` -This guide describes how to configure MicroK8s to work with Zero to Juptyerhub for Kubernetes. +This guide describes how to configure MicroK8s to work with Zero to Jupyterhub for Kubernetes. ## Procedure From 1e3aa6938723a139a09405e301d18b50f27c8fbc Mon Sep 17 00:00:00 2001 From: theomper <31165738+theomper@users.noreply.github.com> Date: Mon, 21 Mar 2022 16:01:53 +0200 Subject: [PATCH 223/898] Update docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md Capitalization fix Co-authored-by: Min RK --- .../kubernetes/other-infrastructure/step-zero-microk8s.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md index 99aeb7cd3c..529032ff67 100644 --- a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md @@ -8,7 +8,7 @@ If you have server hardware available and a small enough user base it's possible With no ability to scale, users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. ``` -This guide describes how to configure MicroK8s to work with Zero to Jupyterhub for Kubernetes. +This guide describes how to configure MicroK8s to work with Zero to JupyterHub for Kubernetes. ## Procedure From 218afa38b8cc554677042a22f5e70e6c319767fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Mar 2022 23:40:39 +0000 Subject: [PATCH 224/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v2.5.1 → v2.6.0](https://github.com/pre-commit/mirrors-prettier/compare/v2.5.1...v2.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c573c7edab..9397303ef8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.5.1 + rev: v2.6.0 hooks: - id: prettier From cc1583ca3136783c45045471cdf8b7f1f2cdcc44 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Wed, 23 Mar 2022 01:40:03 +0000 Subject: [PATCH 225/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 9d92e04881..e27789c4d5 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-03-18_01:25:35 +# VULN_SCAN_TIME=2022-03-23_01:39:59 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 426bd3cc5a930927f4cf48306e2777ac7a2eef60 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Mar 2022 08:42:51 +0100 Subject: [PATCH 226/898] breaking: require k8s 1.20+ (lowest cloud provider supported version) --- jupyterhub/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 8e498436d1..08dd724813 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -8,7 +8,7 @@ keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg -kubeVersion: ">=1.17.0-0" +kubeVersion: ">=1.20.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers # listed, we have added some below, but in practice the entire JupyterHub team From b3ae221a32226ba642f6624398612d03bc394845 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Mar 2022 08:41:46 +0100 Subject: [PATCH 227/898] maint: remove Ingress template conditionals for old k8s versions --- jupyterhub/templates/hub/pdb.yaml | 3 ++- jupyterhub/templates/ingress.yaml | 10 ---------- jupyterhub/templates/proxy/autohttps/pdb.yaml | 3 ++- jupyterhub/templates/proxy/pdb.yaml | 3 ++- .../templates/scheduling/user-placeholder/pdb.yaml | 3 ++- .../templates/scheduling/user-scheduler/pdb.yaml | 3 ++- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/jupyterhub/templates/hub/pdb.yaml b/jupyterhub/templates/hub/pdb.yaml index 00816d78fe..5a73211375 100644 --- a/jupyterhub/templates/hub/pdb.yaml +++ b/jupyterhub/templates/hub/pdb.yaml @@ -1,5 +1,6 @@ {{- if .Values.hub.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} apiVersion: policy/v1beta1 diff --git a/jupyterhub/templates/ingress.yaml b/jupyterhub/templates/ingress.yaml index a86753f85b..91f96f4bf6 100644 --- a/jupyterhub/templates/ingress.yaml +++ b/jupyterhub/templates/ingress.yaml @@ -1,9 +1,5 @@ {{- if .Values.ingress.enabled -}} -{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} apiVersion: networking.k8s.io/v1 -{{- else }} -apiVersion: networking.k8s.io/v1beta1 -{{- end }} kind: Ingress metadata: name: {{ include "jupyterhub.ingress.fullname" . }} @@ -22,18 +18,12 @@ spec: - http: paths: - path: {{ $.Values.hub.baseUrl | trimSuffix "/" }}/{{ $.Values.ingress.pathSuffix }} - {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} pathType: {{ $.Values.ingress.pathType }} backend: service: name: {{ include "jupyterhub.proxy-public.fullname" $ }} port: name: http - {{- else }} - backend: - serviceName: {{ include "jupyterhub.proxy-public.fullname" $ }} - servicePort: 80 - {{- end }} {{- if $host }} host: {{ $host | quote }} {{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/pdb.yaml b/jupyterhub/templates/proxy/autohttps/pdb.yaml index 57fc181b6c..c67a85ac7f 100644 --- a/jupyterhub/templates/proxy/autohttps/pdb.yaml +++ b/jupyterhub/templates/proxy/autohttps/pdb.yaml @@ -1,5 +1,6 @@ {{- if .Values.proxy.traefik.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} apiVersion: policy/v1beta1 diff --git a/jupyterhub/templates/proxy/pdb.yaml b/jupyterhub/templates/proxy/pdb.yaml index c59d53279d..3280ad0fe7 100644 --- a/jupyterhub/templates/proxy/pdb.yaml +++ b/jupyterhub/templates/proxy/pdb.yaml @@ -1,5 +1,6 @@ {{- if .Values.proxy.chp.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} apiVersion: policy/v1beta1 diff --git a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml index 08dcf06389..43499a7eec 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml @@ -3,7 +3,8 @@ The cluster autoscaler should be allowed to evict and reschedule these pods if it would help in order to scale down a node. */}} {{- if .Values.scheduling.userPlaceholder.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} apiVersion: policy/v1beta1 diff --git a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml index c7831e2fb1..47f940c09c 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml @@ -1,5 +1,6 @@ {{- if and .Values.scheduling.userScheduler.enabled .Values.scheduling.userScheduler.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" }} +{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} apiVersion: policy/v1beta1 From f23efca02d1ffd821c810a4d1fdbf3dc67b32bbe Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Mar 2022 08:46:04 +0100 Subject: [PATCH 228/898] ci: stop testing against k8s 1.17-1.19 --- .github/workflows/test-chart.yaml | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 89515d023c..7d5f27fe5d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -124,32 +124,22 @@ jobs: # k3s-version: https://github.com/rancher/k3s/tags # k3s-channel: https://update.k3s.io/v1-release/channels include: - - k3s-channel: v1.23 - test: install - - k3s-channel: v1.22 - test: install - - k3s-channel: v1.21 + - k3s-channel: latest test: install - - k3s-channel: v1.20 + - k3s-channel: stable test: install - - k3s-channel: v1.19 - test: install - - k3s-channel: v1.18 # also test prePuller.hook + - k3s-channel: v1.21 # also test prePuller.hook test: install local-chart-extra-args: >- --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - - k3s-channel: v1.17 # also test hub.existingSecret + - k3s-channel: v1.20 # also test hub.existingSecret test: install local-chart-extra-args: >- --set hub.existingSecret=test-hub-existing-secret --set proxy.secretToken=aaaa1111 --set hub.cookieSecret=bbbb2222 --set hub.config.CryptKeeper.keys[0]=cccc3333 - # ingress.ingressClassName requires k8s 1.18 and above, don't - # validate setting it against the k8s api-server on k8s 1.17. - helm-template-validate-extra-args: >- - --set ingress.ingressClassName="" create-k8s-test-resources: true # We run two upgrade tests where we first install an already released @@ -164,7 +154,7 @@ jobs: # The upgrade-from input should match the version information from # https://jupyterhub.github.io/helm-chart/info.json # - - k3s-channel: v1.19 + - k3s-channel: v1.23 test: upgrade upgrade-from: stable upgrade-from-extra-args: >- @@ -177,7 +167,7 @@ jobs: --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic create-k8s-test-resources: true - - k3s-channel: v1.19 + - k3s-channel: v1.23 test: upgrade upgrade-from: dev upgrade-from-extra-args: >- From b40975e5e8c1bf84db2276d2662218d7a4b28597 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Mar 2022 08:54:08 +0100 Subject: [PATCH 229/898] docs: update k8s version references to modern versions --- docs/source/administrator/advanced.md | 2 +- jupyterhub/schema.yaml | 37 +++++++++++++-------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/source/administrator/advanced.md b/docs/source/administrator/advanced.md index 9c349558d9..30a505c0c6 100644 --- a/docs/source/administrator/advanced.md +++ b/docs/source/administrator/advanced.md @@ -183,7 +183,7 @@ in kubernetes that as a long list of cool use cases. Some example use cases are: 2. Servers / other daemons that are used by code in your `hub.customConfig` The items in this list must be valid kubernetes -[container specifications](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#container-v1-core). +[container specifications](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#container-v1-core). ### Specifying suitable hub storage diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fe7fa53494..eef542a594 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -824,7 +824,7 @@ properties: ``` For more information, see the [Kubernetes EnvVar - specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvar-v1-core). + specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#envvar-v1-core). extraConfig: type: object additionalProperties: true @@ -1005,7 +1005,7 @@ properties: [scheduling.userPods.tolerations](schema_scheduling.userPods.tolerations)). Pass this field an array of - [`Toleration`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#toleration-v1-core) + [`Toleration`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#toleration-v1-core) objects. See the [Kubernetes @@ -1040,7 +1040,7 @@ properties: additionalProperties: true description: | A k8s native specification of the container's security context, see [the - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#securitycontext-v1-core) + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core) for details. deploymentStrategy: type: object @@ -1080,7 +1080,7 @@ properties: flag. See [the k8s - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#probe-v1-core) + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core) for more details. readinessProbe: *probe-spec namedServerLimitPerUser: @@ -1094,13 +1094,13 @@ properties: additionalProperties: true description: | A k8s native specification of resources, see [the - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core). + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#resourcerequirements-v1-core). lifecycle: &lifecycle-spec type: object additionalProperties: false description: | A k8s native specification of lifecycle hooks on the container, see [the - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#lifecycle-v1-core). + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#lifecycle-v1-core). properties: postStart: type: object @@ -1378,7 +1378,7 @@ properties: ``` For more information, see the [Kubernetes EnvVar - specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvar-v1-core). + specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#envvar-v1-core). pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec @@ -1498,7 +1498,7 @@ properties: or the `proxy` pod (chp). See [the Kubernetes - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core) + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#serviceport-v1-core) for the structure of the items in this list. loadBalancerIP: type: [string, "null"] @@ -1664,7 +1664,7 @@ properties: ``` For more information, see the [Kubernetes EnvVar - specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvar-v1-core). + specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#envvar-v1-core). pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec @@ -1899,7 +1899,7 @@ properties: ``` For more information, see the [Kubernetes EnvVar - specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#envvar-v1-core). + specification](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#envvar-v1-core). nodeSelector: *nodeSelector-spec extraTolerations: *tolerations-spec extraNodeAffinity: @@ -1920,13 +1920,13 @@ properties: type: array description: | Pass this field an array of - [`NodeSelectorTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#nodeselectorterm-v1-core) + [`NodeSelectorTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#nodeselectorterm-v1-core) objects. preferred: type: array description: | Pass this field an array of - [`PreferredSchedulingTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#preferredschedulingterm-v1-core) + [`PreferredSchedulingTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#preferredschedulingterm-v1-core) objects. extraPodAffinity: type: object @@ -1938,13 +1938,13 @@ properties: type: array description: | Pass this field an array of - [`PodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podaffinityterm-v1-core) + [`PodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podaffinityterm-v1-core) objects. preferred: type: array description: | Pass this field an array of - [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#weightedpodaffinityterm-v1-core) + [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#weightedpodaffinityterm-v1-core) objects. extraPodAntiAffinity: type: object @@ -1956,13 +1956,13 @@ properties: type: array description: | Pass this field an array of - [`PodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#podaffinityterm-v1-core) + [`PodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podaffinityterm-v1-core) objects. preferred: type: array description: | Pass this field an array of - [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#weightedpodaffinityterm-v1-core) + [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#weightedpodaffinityterm-v1-core) objects. cloudMetadata: type: object @@ -2421,8 +2421,7 @@ properties: ingressClassName: type: [string, "null"] description: | - Maps directly to the Ingress resource's spec.ingressClassName. To - configure this, your k8s cluster must have version 1.18+ or above. + Maps directly to the Ingress resource's `spec.ingressClassName``. See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) @@ -2440,7 +2439,7 @@ properties: pathType: enum: [Prefix, Exact, ImplementationSpecific] description: | - The path type to use. The default value is 'Prefix'. Only applies on Kubernetes v1.18+. + The path type to use. The default value is 'Prefix'. See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) for more details about path types. From 42d0baad3a79961fcb5f66bec00324ef7fb874c4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 23 Mar 2022 09:02:34 +0100 Subject: [PATCH 230/898] docs: fix broken link --- docs/source/resources/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/resources/glossary.md b/docs/source/resources/glossary.md index bf05315bd6..b39c79640d 100644 --- a/docs/source/resources/glossary.md +++ b/docs/source/resources/glossary.md @@ -126,5 +126,5 @@ Additions to the glossary are welcomed. Please add in alphabetical order. A spawner is a separate process created for each active user by JupyterHub. They are each responsible for one user. This Helm chart relies on `KubeSpawner - `_. + `_. ``` From f0e38d18a326bbf5728266a6a6652a6d58f1d5c1 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 24 Mar 2022 01:26:14 +0000 Subject: [PATCH 231/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 7af4530497..5d9c9a60f4 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-03-18_01:24:14 +# VULN_SCAN_TIME=2022-03-24_01:26:12 RUN apk add --no-cache iptables From 954dc5e84b150772fa8ee9038707945121f80910 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 24 Mar 2022 01:26:46 +0000 Subject: [PATCH 232/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index b95209fe8e..ad61da2cbc 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-03-18_01:24:03 +# VULN_SCAN_TIME=2022-03-24_01:26:45 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 08728932b720aca62ef40587ba364236519f373f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Mar 2022 05:06:38 +0000 Subject: [PATCH 233/898] build(deps): bump peter-evans/create-pull-request from 3.14.0 to 4 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 3.14.0 to 4. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/18f7dc018cc2cd597073088f7c7591b9d1c02672...d6d5519d05f5814158ef015b8448f2f74648c421) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index c2db46a726..e6b0aa5806 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@18f7dc018cc2cd597073088f7c7591b9d1c02672 + uses: peter-evans/create-pull-request@d6d5519d05f5814158ef015b8448f2f74648c421 with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From b4e476ac118b6917a557239a523fbf876d7122ec Mon Sep 17 00:00:00 2001 From: Dmitry Goryunov Date: Fri, 25 Mar 2022 17:10:51 +0100 Subject: [PATCH 234/898] Remove CHOWN_HOME --- docs/source/kubernetes/amazon/efs_storage.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/source/kubernetes/amazon/efs_storage.md b/docs/source/kubernetes/amazon/efs_storage.md index e2a9e793f0..ea4096db37 100644 --- a/docs/source/kubernetes/amazon/efs_storage.md +++ b/docs/source/kubernetes/amazon/efs_storage.md @@ -105,16 +105,11 @@ Procedure: static: pvcName: "efs-persist" subPath: "home/{username}" - extraEnv: - CHOWN_HOME: "yes" uid: 0 fsGid: 0 cmd: "start-singleuser.sh" ``` - The image setting overrides the default pinned jh base image since it has not yet been updated - to include the CHOWN_HOME setting. This will be fixed in Z2JH 0.7. - type static tells jh not to use a storage class and instead use a PVC defined below. pvcName matches the claim name we specified before @@ -125,9 +120,6 @@ Procedure: It turns out there is a bug in jupyterhub where the default subPath does not work, and setting the subPath to "{username}" breaks in the same way. - The extraEnv section set's environmental variables before trying to start jupyterhub inside of the user's - container. CHOWN_HOME is needed to force the ownership change of the home directory. - Kubernetes is still conflicted if a uid and a gid should be passed in to change how the directory is mounted inside of the container. What we do for now is auto-chown the directory before jupyterhub has been started. From e5eeedcec3a50d166b4061c56a00adfcbcca82ed Mon Sep 17 00:00:00 2001 From: Dmitry Goryunov Date: Fri, 25 Mar 2022 17:51:48 +0100 Subject: [PATCH 235/898] Update efs_storage.md --- docs/source/kubernetes/amazon/efs_storage.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/source/kubernetes/amazon/efs_storage.md b/docs/source/kubernetes/amazon/efs_storage.md index ea4096db37..f8e1abb6b3 100644 --- a/docs/source/kubernetes/amazon/efs_storage.md +++ b/docs/source/kubernetes/amazon/efs_storage.md @@ -104,22 +104,28 @@ Procedure: type: "static" static: pvcName: "efs-persist" - subPath: "home/{username}" + subPath: "home/{username}" extraEnv: + extraEnv: + CHOWN_HOME: "yes" uid: 0 fsGid: 0 cmd: "start-singleuser.sh" ``` + The image setting overrides the default pinned jh base image since it has not yet been updated + to include the CHOWN_HOME setting. + type static tells jh not to use a storage class and instead use a PVC defined below. pvcName matches the claim name we specified before - subPath tells where on the supplied storage the mount point should be. In this case it will be "$EFS_ROOT/home/{username}" - It turns out there is a bug in jupyterhub where the default subPath does not work, and setting the subPath to "{username}" breaks in the same way. + The extraEnv section set's environmental variables before trying to start jupyterhub inside of the user's + container. CHOWN_HOME is needed to force the ownership change of the home directory. + Kubernetes is still conflicted if a uid and a gid should be passed in to change how the directory is mounted inside of the container. What we do for now is auto-chown the directory before jupyterhub has been started. From 437a9c667b53ff4875d66dd0dabf20294f86dd0c Mon Sep 17 00:00:00 2001 From: Dmitry Goryunov Date: Fri, 25 Mar 2022 17:52:27 +0100 Subject: [PATCH 236/898] Update efs_storage.md --- docs/source/kubernetes/amazon/efs_storage.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/kubernetes/amazon/efs_storage.md b/docs/source/kubernetes/amazon/efs_storage.md index f8e1abb6b3..b1845d35e4 100644 --- a/docs/source/kubernetes/amazon/efs_storage.md +++ b/docs/source/kubernetes/amazon/efs_storage.md @@ -104,7 +104,7 @@ Procedure: type: "static" static: pvcName: "efs-persist" - subPath: "home/{username}" extraEnv: + subPath: "home/{username}" extraEnv: CHOWN_HOME: "yes" uid: 0 @@ -118,8 +118,10 @@ Procedure: type static tells jh not to use a storage class and instead use a PVC defined below. pvcName matches the claim name we specified before + subPath tells where on the supplied storage the mount point should be. In this case it will be "$EFS_ROOT/home/{username}" + It turns out there is a bug in jupyterhub where the default subPath does not work, and setting the subPath to "{username}" breaks in the same way. From 7f08f384d1e0e0c454163b2618eb7a2e03e43055 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 25 Mar 2022 16:52:53 +0000 Subject: [PATCH 237/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/kubernetes/amazon/efs_storage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/kubernetes/amazon/efs_storage.md b/docs/source/kubernetes/amazon/efs_storage.md index b1845d35e4..9005319f17 100644 --- a/docs/source/kubernetes/amazon/efs_storage.md +++ b/docs/source/kubernetes/amazon/efs_storage.md @@ -118,10 +118,10 @@ Procedure: type static tells jh not to use a storage class and instead use a PVC defined below. pvcName matches the claim name we specified before - + subPath tells where on the supplied storage the mount point should be. In this case it will be "$EFS_ROOT/home/{username}" - + It turns out there is a bug in jupyterhub where the default subPath does not work, and setting the subPath to "{username}" breaks in the same way. From 12a9bd655e2876c085b2b9033f1fbefc3730fff5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Mar 2022 19:35:59 +0000 Subject: [PATCH 238/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v2.6.0 → v2.6.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.6.0...v2.6.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9397303ef8..1fa5ecb220 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.0 + rev: v2.6.1 hooks: - id: prettier From eff728095a62c953c592c7e9277f4552bf935630 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 29 Mar 2022 01:44:40 +0000 Subject: [PATCH 239/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 5d9c9a60f4..0e1a083309 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-03-24_01:26:12 +# VULN_SCAN_TIME=2022-03-29_01:44:38 RUN apk add --no-cache iptables From 07b57ae979827d7916d3785f62e9550f8c9b5ed2 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 29 Mar 2022 01:46:01 +0000 Subject: [PATCH 240/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 4a612af416..a7d5063b0c 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-03-18_01:24:56 +# VULN_SCAN_TIME=2022-03-29_01:45:59 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From b6e0397dd7822761757b577a4b13c11fb469cefb Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Tue, 29 Mar 2022 01:47:02 +0000 Subject: [PATCH 241/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index e27789c4d5..42dccefad2 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-03-23_01:39:59 +# VULN_SCAN_TIME=2022-03-29_01:47:00 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From d8ae73485f282ac5b36cb0d04722f16795fdd391 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Wed, 30 Mar 2022 01:40:37 +0000 Subject: [PATCH 242/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index ad61da2cbc..2d2dd5d253 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-03-24_01:26:45 +# VULN_SCAN_TIME=2022-03-30_01:40:36 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From ed4998b3041280a961e52d9eb6508853fc9e18f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 05:06:29 +0000 Subject: [PATCH 243/898] build(deps): bump peter-evans/create-pull-request from 4.0.0 to 4.0.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/d6d5519d05f5814158ef015b8448f2f74648c421...f1a7646cead32c950d90344a4fb5d4e926972a8f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index e6b0aa5806..36f355a5f6 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@d6d5519d05f5814158ef015b8448f2f74648c421 + uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From ab336d7e3e3003b09524ddf64a9a5fc816211770 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:24:26 +0000 Subject: [PATCH 244/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.1.0 → 22.3.0](https://github.com/psf/black/compare/22.1.0...22.3.0) - [github.com/pre-commit/mirrors-prettier: v2.6.1 → v2.6.2](https://github.com/pre-commit/mirrors-prettier/compare/v2.6.1...v2.6.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1fa5ecb220..bb5d8e2ae3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.3.0 hooks: - id: black args: [--target-version=py36] @@ -42,7 +42,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.1 + rev: v2.6.2 hooks: - id: prettier From e2332122c56d895347316191a76438ce9aefa33e Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 11 Apr 2022 01:44:29 +0000 Subject: [PATCH 245/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 0e1a083309..a5a28c4d00 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-03-29_01:44:38 +# VULN_SCAN_TIME=2022-04-11_01:44:28 RUN apk add --no-cache iptables From a9e9a3f3b38d4294240ef12392f1e88dec61eeab Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 11 Apr 2022 01:45:05 +0000 Subject: [PATCH 246/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 2d2dd5d253..c171ca520c 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-03-30_01:40:36 +# VULN_SCAN_TIME=2022-04-11_01:45:04 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From e6ccbb10001a7ccb2aa30bd39dcb6ea394861558 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 11 Apr 2022 01:45:52 +0000 Subject: [PATCH 247/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index a7d5063b0c..b3a5990a66 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-03-29_01:45:59 +# VULN_SCAN_TIME=2022-04-11_01:45:50 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 2541a7327d17bd537ef7bfd6bc9c6496d84485d4 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 11 Apr 2022 01:46:49 +0000 Subject: [PATCH 248/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 42dccefad2..24196190f1 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-03-29_01:47:00 +# VULN_SCAN_TIME=2022-04-11_01:46:47 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 3d8639c4cb02ddeaf3ff9908bde87f19630f1ec4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 05:04:21 +0000 Subject: [PATCH 249/898] build(deps): bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 627ce50c54..c95873f415 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -146,7 +146,7 @@ jobs: run: helm package jupyterhub # ref: https://github.com/actions/upload-artifact - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 if: steps.publishing.outputs.publishing == '' with: name: jupyterhub-${{ github.sha }} From 133a7e73e9f1d3445eca41616731940faec38af8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 05:04:24 +0000 Subject: [PATCH 250/898] build(deps): bump aquasecurity/trivy-action from 0.2.2 to 0.2.3 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.2 to 0.2.3. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/a7a829a4345428ddd92ca57b18257440f6a18c90...40c4ca9e7421287d0c5576712fdff370978f9c3c) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 36f355a5f6..d5bdf65a18 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 + uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 + uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@a7a829a4345428ddd92ca57b18257440f6a18c90 + uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c with: image-ref: rebuilt-image format: table From 06388c27a8cfe3e2c99d8e78c5abafc99a49d534 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 05:04:28 +0000 Subject: [PATCH 251/898] build(deps): bump peter-evans/create-pull-request from 4.0.1 to 4.0.2 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/f1a7646cead32c950d90344a4fb5d4e926972a8f...bd72e1b7922d417764d27d30768117ad7da78a0e) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 36f355a5f6..8ab8ceb725 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f + uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From 84af40eed9283fb23afaf3672cd0a110b2856243 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Apr 2022 21:26:16 +0000 Subject: [PATCH 252/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.31.1 → v2.32.0](https://github.com/asottile/pyupgrade/compare/v2.31.1...v2.32.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb5d8e2ae3..7789bbf59c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.31.1 + rev: v2.32.0 hooks: - id: pyupgrade args: From 4f883b9f370650ad7daf7a476e1ed952e2ca0f94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 14 Apr 2022 05:09:08 +0000 Subject: [PATCH 253/898] build(deps): bump aquasecurity/trivy-action from 0.2.3 to 0.2.4 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.3 to 0.2.4. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/40c4ca9e7421287d0c5576712fdff370978f9c3c...d7a51817e8b3f162f53042add238ac1dcc7a8144) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index e78cffb810..0cfdb76db7 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c + uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c + uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@40c4ca9e7421287d0c5576712fdff370978f9c3c + uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 with: image-ref: rebuilt-image format: table From 7cd163ff3e16d80824f8328c7905e2d85b37e0ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 05:06:52 +0000 Subject: [PATCH 254/898] build(deps): bump aquasecurity/trivy-action from 0.2.4 to 0.2.5 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.4 to 0.2.5. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/d7a51817e8b3f162f53042add238ac1dcc7a8144...2b30463ddb3d11724a04e760e020c7d9af24d8b3) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0cfdb76db7..cdd95cd885 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 + uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 + uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@d7a51817e8b3f162f53042add238ac1dcc7a8144 + uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 with: image-ref: rebuilt-image format: table From 1ff36abf788254f4be3716bac8b1bd0fcc4cf923 Mon Sep 17 00:00:00 2001 From: Greg Atkins Date: Tue, 19 Apr 2022 13:53:26 +0100 Subject: [PATCH 255/898] Add extra init containers to chart --- jupyterhub/schema.yaml | 16 ++++++++++++++++ .../templates/proxy/autohttps/deployment.yaml | 3 +++ jupyterhub/values.yaml | 1 + 3 files changed, 20 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index eef542a594..e847b64c06 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1628,6 +1628,22 @@ properties: See the [Kubernetes docs](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) to learn more about labels. networkPolicy: *networkPolicy-spec + extraInitContainers: + type: array + description: | + list of extraInitContainers to be run with traefik pod, after the containers set in the chart. See [Kubernetes Docs](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) + + ```yaml + proxy: + traefik: + extraInitContainers: + - name: init-myservice + image: busybox:1.28 + command: ['sh', '-c', 'command1'] + - name: init-mydb + image: busybox:1.28 + command: ['sh', '-c', 'command2'] + ``` extraEnv: type: [object, array] additionalProperties: true diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 4853c64ddb..bf5927c66c 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -79,6 +79,9 @@ spec: securityContext: {{- . | toYaml | nindent 12 }} {{- end }} + {{- with .Values.proxy.traefik.initContainers }} + {{- . | toYaml | nindent 8 }} + {{- end }} containers: - name: traefik image: "{{ .Values.proxy.traefik.image.name }}:{{ .Values.proxy.traefik.image.tag }}" diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5e7d68b399..45ecdc7a00 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -248,6 +248,7 @@ proxy: maxAge: 15724800 # About 6 months resources: {} labels: {} + extraInitContainers: [] extraEnv: {} extraVolumes: [] extraVolumeMounts: [] From ec108c715b9cfad8044727c59f40d08008f1b12b Mon Sep 17 00:00:00 2001 From: Greg Atkins Date: Tue, 19 Apr 2022 15:18:58 +0100 Subject: [PATCH 256/898] Full rename --- jupyterhub/templates/proxy/autohttps/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index bf5927c66c..df1e032246 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -79,7 +79,7 @@ spec: securityContext: {{- . | toYaml | nindent 12 }} {{- end }} - {{- with .Values.proxy.traefik.initContainers }} + {{- with .Values.proxy.traefik.extraInitContainers }} {{- . | toYaml | nindent 8 }} {{- end }} containers: From 80ac5eabc586ac4b5b9f30ab3612a836c8da6dd5 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 24 Apr 2022 23:28:19 +0100 Subject: [PATCH 257/898] `proxy.service.type`: link directly to k8s docs The help text refers to `hub.service.type`, but this isn't hyperlinked, and when you do find it you're taken to the k8s docs anyway. --- jupyterhub/schema.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index eef542a594..bdd75dfad7 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1446,7 +1446,9 @@ properties: type: enum: [ClusterIP, NodePort, LoadBalancer, ExternalName] description: | - Default `LoadBalancer`. See `hub.service.type` for supported values. + Default `LoadBalancer`. + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) + to learn more about service types. labels: type: object additionalProperties: false From 8f8ab44da008ac4a7ba9b8bd62de4790492d19f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Apr 2022 00:40:00 +0000 Subject: [PATCH 258/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 3.0.2 to 4.0.0. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/3.0.2...4.0.0) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index adf6bfdf8c..6533acafcf 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -87,7 +87,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==3.0.2 +jupyterhub-kubespawner==4.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From a2e1b4f1a4549315c643d3634edd4deeb1eb9e2c Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 25 Apr 2022 01:46:03 +0000 Subject: [PATCH 259/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index c171ca520c..3cf50e1108 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-04-11_01:45:04 +# VULN_SCAN_TIME=2022-04-25_01:46:01 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From d332a691b8c8fc1f8afe06cc957e1a3c028fcace Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 25 Apr 2022 01:46:48 +0000 Subject: [PATCH 260/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index b3a5990a66..cff777340d 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-04-11_01:45:50 +# VULN_SCAN_TIME=2022-04-25_01:46:46 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From db394eaf87dcd4c9e35f4b0a14bdb98319d663b8 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Mon, 25 Apr 2022 01:47:27 +0000 Subject: [PATCH 261/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 24196190f1..0e5552ac48 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-04-11_01:46:47 +# VULN_SCAN_TIME=2022-04-25_01:47:25 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From ff5e2472aaa8aa9e8e5a848fd6732cae0cb085a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Apr 2022 05:07:58 +0000 Subject: [PATCH 262/898] build(deps): bump docker/setup-buildx-action from 1.6.0 to 1.7.0 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1.6.0 to 1.7.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/94ab11c41e45d028884a99163086648e898eed25...f211e3e9ded2d9377c8cadc4489a4e38014bc4c9) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c95873f415..d583ad6e99 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -75,7 +75,7 @@ jobs: uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # dependabot updates to latest release + uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 9bbbee20ae..a0c1e0a09d 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -46,7 +46,7 @@ jobs: uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # dependabot updates to latest release + uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release - name: Build a multiple architecture Docker image run: >- From b0f5a4485cd8c27b6619e2954c533f8e75816ba6 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 5 May 2022 01:51:21 +0000 Subject: [PATCH 263/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index cff777340d..eb5ed6f9f3 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-04-25_01:46:46 +# VULN_SCAN_TIME=2022-05-05_01:51:19 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 5872a20182d459349b3a8cd3cd2a6067fb863142 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Thu, 5 May 2022 01:52:30 +0000 Subject: [PATCH 264/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 0e5552ac48..57025590b0 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-04-25_01:47:25 +# VULN_SCAN_TIME=2022-05-05_01:52:28 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 4152df1e7c8775d61e71bac8fd5f920a8c204e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B0=BD=ED=99=98?= Date: Thu, 10 Feb 2022 14:58:51 +0900 Subject: [PATCH 265/898] Implement continuous image puller priority --- jupyterhub/schema.yaml | 11 +++++++++++ jupyterhub/templates/_helpers-names.tpl | 9 +++++++++ .../templates/image-puller/_helpers-daemonset.tpl | 2 +- jupyterhub/values.yaml | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 5e7df7c1ea..ad066f0db1 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2259,6 +2259,11 @@ properties: 4. Normal user pods have a higher priority than the user-placeholder pods + Additionally, continuous image puller pods should get a priority + between normal user pods and placeholder pods. Otherwise, continuous + image puller pods may block normal user pods to spawn, and placeholder + pods may block continuous image puller pods. + Note that if the default priority cutoff if not configured on cluster autoscaler, it will currently default to 0, and that in the future this is meant to be lowered. If your cloud provider is installing the @@ -2273,6 +2278,7 @@ properties: enabled: true globalDefault: false defaultPriority: 0 + continuousImagePullerPriority: -5 userPlaceholderPriority: -10 ``` @@ -2283,6 +2289,7 @@ properties: enabled: true globalDefault: true defaultPriority: 10 + continuousImagePullerPriority: 5 userPlaceholderPriority: 0 ``` properties: @@ -2301,6 +2308,10 @@ properties: type: integer description: | The actual value for the default pod priority. + continuousImagePullerPriority: + type: integer + description: | + The actual value for the continuous-image-puller pods' priority. userPlaceholderPriority: type: integer description: | diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 6ba4b5b2ac..7d4e136002 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -210,6 +210,15 @@ {{- end }} {{- end }} +{{- /* continuous-image-puller.fullname Priority */}} +{{- define "jupyterhub.continuous-image-puller-priority.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.continuous-image-puller.fullname" . }} + {{- else }} + {{- .Release.Name }}-continuous-image-puller-priority + {{- end }} +{{- end }} + {{- /* user-scheduler's registered name */}} {{- define "jupyterhub.user-scheduler.fullname" -}} {{- if (include "jupyterhub.fullname" .) }} diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index 434ac6300f..e0703dcd29 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -48,7 +48,7 @@ spec: per node limit all k8s clusters have. */}} {{- if and (not .hook) .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} + priorityClassName: {{ include "jupyterhub.continuous-image-puller-priority.fullname" . }} {{- end }} {{- with .Values.singleuser.nodeSelector }} nodeSelector: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 45ecdc7a00..c8741782ec 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -507,6 +507,7 @@ scheduling: enabled: false globalDefault: false defaultPriority: 0 + continuousImagePullerPriority: -5 userPlaceholderPriority: -10 userPlaceholder: enabled: true From 007ac3725d0e3e9b687c5256ce48b74163e7cf80 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 6 May 2022 01:43:01 +0000 Subject: [PATCH 266/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index eb5ed6f9f3..5960159aaa 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-05-05_01:51:19 +# VULN_SCAN_TIME=2022-05-06_01:43:00 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From 73387dcfbcbadfc444c714a8f478187ca745c0ff Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Fri, 6 May 2022 01:44:00 +0000 Subject: [PATCH 267/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 57025590b0..49792cca6f 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-05-05_01:52:28 +# VULN_SCAN_TIME=2022-05-06_01:43:58 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From 95217cfa51367d463d3471a1cb01b53d60290474 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 05:04:05 +0000 Subject: [PATCH 268/898] build(deps): bump docker/setup-qemu-action from 1.2.0 to 2 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1.2.0 to 2. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/27d0a4f181a40b142cce983c5393082c365d1480...8b122486cedac8393e77aa9734c3528886e4a1a8) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d583ad6e99..2bad8034b8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -72,7 +72,7 @@ jobs: print(f"::set-output name=publishing::{publishing}") - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release + uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index a0c1e0a09d..7035ff60e1 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -43,7 +43,7 @@ jobs: run: pip install chartpress - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@27d0a4f181a40b142cce983c5393082c365d1480 # dependabot updates to latest release + uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release From e91df313cbe8fdd04a20dcb92af1a837e0b15fe3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 May 2022 06:49:45 +0000 Subject: [PATCH 269/898] build(deps): bump docker/setup-buildx-action from 1.7.0 to 2 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1.7.0 to 2. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/f211e3e9ded2d9377c8cadc4489a4e38014bc4c9...dc7b9719a96d48369863986a06765841d7ea23f6) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2bad8034b8..43fc2f6149 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -75,7 +75,7 @@ jobs: uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release + uses: docker/setup-buildx-action@dc7b9719a96d48369863986a06765841d7ea23f6 # dependabot updates to latest release - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 7035ff60e1..7ba90a8f90 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -46,7 +46,7 @@ jobs: uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@f211e3e9ded2d9377c8cadc4489a4e38014bc4c9 # dependabot updates to latest release + uses: docker/setup-buildx-action@dc7b9719a96d48369863986a06765841d7ea23f6 # dependabot updates to latest release - name: Build a multiple architecture Docker image run: >- From a2546a9a8aae48e2a89536f2183a7bd0f897543b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 5 May 2022 12:39:53 +0200 Subject: [PATCH 270/898] Complement PR: add PriorityClass, adjust config name, update docs --- jupyterhub/schema.yaml | 39 ++++++++++--------- jupyterhub/templates/_helpers-names.tpl | 9 +++-- .../image-puller/_helpers-daemonset.tpl | 9 +++-- .../templates/image-puller/priorityclass.yaml | 18 +++++++++ jupyterhub/values.yaml | 2 +- 5 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 jupyterhub/templates/image-puller/priorityclass.yaml diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ad066f0db1..b9975d4309 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2248,21 +2248,22 @@ properties: type: object additionalProperties: false description: | - Pod Priority is used to allow real users evict placeholder pods that - in turn triggers a scale up by a cluster autoscaler. So, enabling this - option will only make sense if the following conditions are met: - - 1. Your Kubernetes cluster has at least version 1.11 - 2. A cluster autoscaler is installed - 3. user-placeholer pods is configured to get a priority equal or - higher than the cluster autoscaler's priority cutoff - 4. Normal user pods have a higher priority than the user-placeholder - pods - - Additionally, continuous image puller pods should get a priority - between normal user pods and placeholder pods. Otherwise, continuous - image puller pods may block normal user pods to spawn, and placeholder - pods may block continuous image puller pods. + Pod Priority is used to allow real users evict user placeholder pods + that in turn by entering a Pending state can trigger a scale up by a + cluster autoscaler. + + Having this option enabled only make sense if the following conditions + are met: + + 1. A cluster autoscaler is installed. + 2. user-placeholer pods are configured to have a priority equal or + higher than the cluster autoscaler's "priority cutoff" so that the + cluster autoscaler scales up a node in advance for a pending user + placeholder pod. + 3. Normal user pods have a higher priority than the user-placeholder + pods. + 4. Image puller pods have a priority between normal user pods and + user-placeholder pods. Note that if the default priority cutoff if not configured on cluster autoscaler, it will currently default to 0, and that in the future @@ -2278,7 +2279,7 @@ properties: enabled: true globalDefault: false defaultPriority: 0 - continuousImagePullerPriority: -5 + imagePullerPriority: -5 userPlaceholderPriority: -10 ``` @@ -2289,7 +2290,7 @@ properties: enabled: true globalDefault: true defaultPriority: 10 - continuousImagePullerPriority: 5 + imagePullerPriority: 5 userPlaceholderPriority: 0 ``` properties: @@ -2308,10 +2309,10 @@ properties: type: integer description: | The actual value for the default pod priority. - continuousImagePullerPriority: + imagePullerPriority: type: integer description: | - The actual value for the continuous-image-puller pods' priority. + The actual value for the [hook|continuous]-image-puller pods' priority. userPlaceholderPriority: type: integer description: | diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 7d4e136002..4d270bf7af 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -210,12 +210,12 @@ {{- end }} {{- end }} -{{- /* continuous-image-puller.fullname Priority */}} -{{- define "jupyterhub.continuous-image-puller-priority.fullname" -}} +{{- /* image-puller Priority */}} +{{- define "jupyterhub.image-puller-priority.fullname" -}} {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.continuous-image-puller.fullname" . }} + {{- include "jupyterhub.fullname.dash" . }}image-puller {{- else }} - {{- .Release.Name }}-continuous-image-puller-priority + {{- .Release.Name }}-image-puller-priority {{- end }} {{- end }} @@ -253,6 +253,7 @@ autohttps: {{ include "jupyterhub.autohttps.fullname" . | quote }} user-scheduler-deploy: {{ include "jupyterhub.user-scheduler-deploy.fullname" . | quote }} user-scheduler-lock: {{ include "jupyterhub.user-scheduler-lock.fullname" . | quote }} user-placeholder: {{ include "jupyterhub.user-placeholder.fullname" . | quote }} +image-puller-priority: {{ include "jupyterhub.image-puller-priority.fullname" . | quote }} hook-image-awaiter: {{ include "jupyterhub.hook-image-awaiter.fullname" . | quote }} hook-image-puller: {{ include "jupyterhub.hook-image-puller.fullname" . | quote }} continuous-image-puller: {{ include "jupyterhub.continuous-image-puller.fullname" . | quote }} diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index e0703dcd29..86e26e5436 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -44,11 +44,12 @@ spec: {{- end }} spec: {{- /* - continuous-image-puller pods are made evictable to save on the k8s pods - per node limit all k8s clusters have. + image-puller pods are made evictable to save on the k8s pods + per node limit all k8s clusters have and have a higher priority + than user-placeholder pods that could block an entire node. */}} - {{- if and (not .hook) .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.continuous-image-puller-priority.fullname" . }} + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.image-puller-priority.fullname" . }} {{- end }} {{- with .Values.singleuser.nodeSelector }} nodeSelector: diff --git a/jupyterhub/templates/image-puller/priorityclass.yaml b/jupyterhub/templates/image-puller/priorityclass.yaml new file mode 100644 index 0000000000..b2dbae0735 --- /dev/null +++ b/jupyterhub/templates/image-puller/priorityclass.yaml @@ -0,0 +1,18 @@ +{{- if .Values.scheduling.podPriority.enabled }} +{{- if or .Values.prePuller.hook.enabled .Values.prePuller.continuous.enabled -}} +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: {{ include "jupyterhub.image-puller-priority.fullname" . }} + annotations: + meta.helm.sh/release-name: "{{ .Release.Name }}" + meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +value: {{ .Values.scheduling.podPriority.imagePullerPriority }} +globalDefault: false +description: >- + Enables [hook|continuous]-image-puller pods to fit on nodes even though they + are clogged by user-placeholder pods, while not evicting normal user pods. +{{- end }} +{{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c8741782ec..330adbd808 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -507,7 +507,7 @@ scheduling: enabled: false globalDefault: false defaultPriority: 0 - continuousImagePullerPriority: -5 + imagePullerPriority: -5 userPlaceholderPriority: -10 userPlaceholder: enabled: true From ffeaf988458b65bb84f3858a2f7f389935d8413b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 05:07:38 +0000 Subject: [PATCH 271/898] build(deps): bump peter-evans/create-pull-request from 4.0.2 to 4.0.3 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.2 to 4.0.3. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/bd72e1b7922d417764d27d30768117ad7da78a0e...f094b77505fb89581e68a1163fbd2fffece39da1) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index cdd95cd885..bdc779df22 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -195,7 +195,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@bd72e1b7922d417764d27d30768117ad7da78a0e + uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 with: token: "${{ secrets.GITHUB_TOKEN }}" author: jupyterhub vuln-scan bot From 79a1b7517b592860d6211d117a28a344dae1b415 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 9 May 2022 20:58:11 +0200 Subject: [PATCH 272/898] Bump JupyterHub from 2.2.0 to 2.3.0 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 4562689654..d253d8f7f5 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,7 @@ # # JupyterHub itself -jupyterhub==2.2.0 +jupyterhub==2.3.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 6533acafcf..b6a24d9907 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -71,7 +71,7 @@ jsonschema==4.4.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.2.0 +jupyterhub==2.3.0 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index b47a394d04..cb19bb20d1 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.2.0 +jupyterhub==2.3.0 nbgitpuller==1.1.0 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 08dd724813..e627268b45 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "2.2.0" +appVersion: "2.3.0" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 8d0a76165982617eb99676f1e32e622f59b172bc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 9 May 2022 21:04:52 +0200 Subject: [PATCH 273/898] hub image: refreeze dependencies --- images/hub/requirements.txt | 77 +++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b6a24d9907..dca59f75ee 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.1 # via kubernetes-asyncio aiosignal==1.2.0 # via aiohttp -alembic==1.7.6 +alembic==1.7.7 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -18,7 +18,7 @@ attrs==21.4.0 # via # aiohttp # jsonschema -bcrypt==3.2.0 +bcrypt==3.2.2 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator @@ -36,7 +36,7 @@ charset-normalizer==2.0.12 # via # aiohttp # requests -cryptography==36.0.1 +cryptography==37.0.2 # via # josepy # jwcrypto @@ -61,26 +61,22 @@ idna==3.3 # via # requests # yarl -jinja2==3.0.3 +importlib-metadata==4.11.3 + # via alembic +importlib-resources==5.7.1 + # via + # alembic + # jsonschema +jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -josepy==1.12.0 +josepy==1.13.0 # via jupyterhub-ltiauthenticator -jsonschema==4.4.0 +jsonschema==4.5.1 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.3.0 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator jupyterhub-firstuseauthenticator==1.0.0 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -97,15 +93,25 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jwcrypto==1.0 +jupyterhub==2.3.0 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator +jwcrypto==1.2 # via jupyterhub-ltiauthenticator -kubernetes-asyncio==21.7.1 +kubernetes-asyncio==22.6.4 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.1.6 +mako==1.2.0 # via alembic -markupsafe==2.1.0 +markupsafe==2.1.1 # via # jinja2 # mako @@ -135,7 +141,7 @@ pamela==1.0.0 # via jupyterhub pem==21.2.0 # via jupyterhub-ltiauthenticator -prometheus-client==0.13.1 +prometheus-client==0.14.1 # via jupyterhub psycopg2-binary==2.9.3 # via -r requirements.in @@ -164,7 +170,7 @@ pyopenssl==22.0.0 # via # certipy # josepy -pyparsing==3.0.7 +pyparsing==3.0.8 # via packaging pyrsistent==0.18.1 # via jsonschema @@ -175,39 +181,38 @@ python-dateutil==2.8.2 # kubernetes-asyncio python-json-logger==2.0.2 # via jupyter-telemetry -python-slugify==6.1.1 +python-slugify==6.1.2 # via jupyterhub-kubespawner pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio +requests-oauthlib==1.3.1 + # via mwoauth requests==2.27.1 # via # jupyterhub # mwoauth # pyjwkest # requests-oauthlib -requests-oauthlib==1.3.1 - # via mwoauth -ruamel-yaml==0.17.21 +ruamel.yaml.clib==0.2.6 + # via ruamel.yaml +ruamel.yaml==0.17.21 # via jupyter-telemetry -ruamel-yaml-clib==0.2.6 - # via ruamel-yaml six==1.16.0 # via - # bcrypt # kubernetes-asyncio # mwoauth # onetimepass # pyjwkest # python-dateutil -sqlalchemy==1.4.32 +sqlalchemy-cockroachdb==1.4.3 + # via -r requirements.in +sqlalchemy==1.4.36 # via # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.3 - # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -222,15 +227,19 @@ traitlets==5.1.1 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.8 +urllib3==1.26.9 # via # jupyterhub-kubespawner # kubernetes-asyncio # requests -wrapt==1.14.0 +wrapt==1.14.1 # via deprecated yarl==1.7.2 # via aiohttp +zipp==3.8.0 + # via + # importlib-metadata + # importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools From 8bcb5912ff9cc039a5b020a953a7c94623563a0b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 20:55:21 +0000 Subject: [PATCH 274/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.32.0 → v2.32.1](https://github.com/asottile/pyupgrade/compare/v2.32.0...v2.32.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7789bbf59c..6656e099ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.32.0 + rev: v2.32.1 hooks: - id: pyupgrade args: From 7b6d2bd3562901fd2e91f5077c492d1ebce55d7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 May 2022 05:06:45 +0000 Subject: [PATCH 275/898] build(deps): bump aquasecurity/trivy-action from 0.2.5 to 0.3.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.2.5 to 0.3.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/2b30463ddb3d11724a04e760e020c7d9af24d8b3...4b9b6fb4ef28b31450391a93ade098bb00de584e) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index bdc779df22..1a780f088f 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -79,7 +79,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 + uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -102,7 +102,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 + uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -161,7 +161,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@2b30463ddb3d11724a04e760e020c7d9af24d8b3 + uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e with: image-ref: rebuilt-image format: table From 446243041da85b318ae41f5a3ab51d5bd733be45 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sun, 15 May 2022 02:03:53 +0000 Subject: [PATCH 276/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 5960159aaa..0dcf17c322 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-05-06_01:43:00 +# VULN_SCAN_TIME=2022-05-15_02:03:52 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From fdda1c0d4cc4548b7bc3c7bf7178d7602163ab37 Mon Sep 17 00:00:00 2001 From: jupyterhub vuln-scan bot Date: Sun, 15 May 2022 02:03:59 +0000 Subject: [PATCH 277/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 49792cca6f..4177c7d351 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-05-06_01:43:58 +# VULN_SCAN_TIME=2022-05-15_02:03:57 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From de603582f6e63780cbec28c6c55c221a09c595ed Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 12:47:46 +0200 Subject: [PATCH 278/898] ci: add automation to update chp and traefik images --- .../workflows/watch-image-dependencies.yaml | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/watch-image-dependencies.yaml diff --git a/.github/workflows/watch-image-dependencies.yaml b/.github/workflows/watch-image-dependencies.yaml new file mode 100644 index 0000000000..e61a853062 --- /dev/null +++ b/.github/workflows/watch-image-dependencies.yaml @@ -0,0 +1,89 @@ +# This is a GitHub workflow defining a set of jobs with a set of steps. +# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# +# - Watch the proxy.chp.image.tag field of values.yaml to match the latest +# jupyterhub/configurable-http-proxy image tag. +# +# - Watch the proxy.traefik.image.tag field of values.yaml to match the latest +# traefik image tag. +# +name: Watch image dependencies + +on: + schedule: + # Run at 05:00 every day, ref: https://crontab.guru/#0_5_*_*_* + - cron: "0 5 * * *" + workflow_dispatch: + +jobs: + update-image-dependencies: + # Don't run this job on forks + if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' + runs-on: ubuntu-20.04 + + # Write permissions granted for the peter-evans/create-pull-request action + # to push to a branch and create/update a PR + permissions: + contents: write + pull-requests: write + + strategy: + fail-fast: false + matrix: + include: + - name: chp + registry: registry.hub.docker.com + repository: jupyterhub/configurable-http-proxy + values_path: proxy.chp.image.tag + - name: traefik + registry: registry.hub.docker.com + repository: library/traefik + values_path: proxy.traefik.image.tag + + steps: + - uses: actions/checkout@v3 + + - name: Get values.yaml pinned tag of ${{ matrix.registry }}/${{ matrix.repository }} + id: local + run: | + local_tag=$(cat jupyterhub/values.yaml | yq e '.${{ matrix.values_path }}' -) + echo "::set-output name=tag::$local_tag" + + - name: Get latest tag of ${{ matrix.registry }}/${{ matrix.repository }} + id: latest + # The skopeo image helps us list tags consistently from different docker + # registries. We use jq to filter out tags of the x.y.z format with the + # optional v prefix, and then sort based on the numerical x, y, and z + # values. Finally, we pick the last value in the list. + # + run: | + latest_tag=$( + docker run --rm quay.io/skopeo/stable list-tags docker://${{ matrix.registry }}/${{ matrix.repository }} \ + | jq -r '[.Tags[] | select(. | test("^v?\\d+\\.\\d+\\.\\d+$"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' + ) + echo "::set-output name=tag::$latest_tag" + + - name: Update values.yaml pinned tag + run: sed --in-place 's/${{ steps.local.outputs.tag }}/${{ steps.latest.outputs.tag }}/g' jupyterhub/values.yaml + + - name: git diff + run: git --no-pager diff --color=always + + # ref: https://github.com/peter-evans/create-pull-request + - name: Create a PR + uses: peter-evans/create-pull-request@v4.0.3 + with: + token: "${{ secrets.github_token }}" + branch: update-image-${{ matrix.name }} + labels: maintenance,dependencies + commit-message: Update ${{ matrix.repository }} version to ${{ steps.latest.outputs.tag }} + title: Update ${{ matrix.repository }} version to ${{ steps.latest.outputs.tag }} + body: >- + A new ${{ matrix.repository }} image version has been detected, version + `${{ steps.latest.outputs.tag }}`. + + + Please close and reopen this PR to run tests for now. This PR was + opened with a `secrets.github_token` and will therefore not trigger + other workflows to run. This can be resolved if we create a bot + account and use its personal access token instead. From 871e0f4a171830934050562a1e7ba7ad8a3b7d0c Mon Sep 17 00:00:00 2001 From: consideRatio Date: Mon, 16 May 2022 11:55:14 +0000 Subject: [PATCH 279/898] Update library/traefik version to v2.6.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 330adbd808..5388c6b296 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -239,7 +239,7 @@ proxy: allowPrivilegeEscalation: false image: name: traefik - tag: v2.6.1 # ref: https://hub.docker.com/_/traefik?tab=tags + tag: v2.6.6 # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 0b6199b67814d227786bbfdc810306b73d178bb1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 14:33:03 +0200 Subject: [PATCH 280/898] refactor: fix helm template indentation --- jupyterhub/templates/proxy/autohttps/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index df1e032246..59303f7e69 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -79,9 +79,9 @@ spec: securityContext: {{- . | toYaml | nindent 12 }} {{- end }} - {{- with .Values.proxy.traefik.extraInitContainers }} + {{- with .Values.proxy.traefik.extraInitContainers }} {{- . | toYaml | nindent 8 }} - {{- end }} + {{- end }} containers: - name: traefik image: "{{ .Values.proxy.traefik.image.name }}:{{ .Values.proxy.traefik.image.tag }}" From e95669c558662bbdb4b16da906cfa69877b9e33e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 14:35:37 +0200 Subject: [PATCH 281/898] ci: update link --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 43fc2f6149..c46ad6b7c3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # name: Publish diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 7d5f27fe5d..5afbbff96d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # name: Test chart diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 7ba90a8f90..ae98e25ad4 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # name: Test docker multiarch build diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 4f5753b147..ae30c63891 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # name: Test docs diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 1a780f088f..493a095c42 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -1,5 +1,5 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. -# ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions +# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions # # This workflow use aquasecurity/trivy to scan the images we have published for # known vulnerabilities. If there are such that can be patched, we let this From 20908cddd25b82546f687390a9dd2ab84e578c61 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 15:00:54 +0200 Subject: [PATCH 282/898] ci: stop dependabot from bumping jupyterhub in 1/4 locations --- .github/dependabot.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 08f41576e5..6491329d5d 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -9,7 +9,6 @@ # - We explicitly set the "maintenance" label to help our changelog generator # tool github-activity to categorize PRs. # - version: 2 updates: # Maintain Python dependencies for the jupyterhub/k8s-hub image @@ -17,7 +16,7 @@ updates: directory: "/images/hub" schedule: interval: daily - time: "00:00" + time: "05:00" timezone: "Etc/UTC" versioning-strategy: lockfile-only labels: @@ -31,12 +30,18 @@ updates: interval: daily time: "05:00" timezone: "Etc/UTC" + # ignore is applied as we need to bump this version in multiple locations, + # and not just in the singleuser-sample image, for which we have a dedicated + # job in the watch-dependencies.yaml workflow file. + # + ignore: + - jupyterhub labels: - maintenance - dependencies # Maintain dependencies in our GitHub Workflows - - package-ecosystem: "github-actions" + - package-ecosystem: github-actions directory: "/" # This should be / rather than .github/workflows schedule: interval: daily From 16020caff41fcd89c38a406af29b7fa02beadb7c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 15:01:21 +0200 Subject: [PATCH 283/898] Rename watch-image-dependencies to watch-dependencies --- .../{watch-image-dependencies.yaml => watch-dependencies.yaml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{watch-image-dependencies.yaml => watch-dependencies.yaml} (99%) diff --git a/.github/workflows/watch-image-dependencies.yaml b/.github/workflows/watch-dependencies.yaml similarity index 99% rename from .github/workflows/watch-image-dependencies.yaml rename to .github/workflows/watch-dependencies.yaml index e61a853062..e91dbac627 100644 --- a/.github/workflows/watch-image-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -7,7 +7,7 @@ # - Watch the proxy.traefik.image.tag field of values.yaml to match the latest # traefik image tag. # -name: Watch image dependencies +name: Watch dependencies on: schedule: From 6e73be4863b2f923b27417a2937fe6c3b3fd5fe1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 15:19:49 +0200 Subject: [PATCH 284/898] ci: add automation to update pinned version of jupyterhub --- .github/workflows/watch-dependencies.yaml | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index e91dbac627..06736ac8cc 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -83,6 +83,64 @@ jobs: `${{ steps.latest.outputs.tag }}`. + Please close and reopen this PR to run tests for now. This PR was + opened with a `secrets.github_token` and will therefore not trigger + other workflows to run. This can be resolved if we create a bot + account and use its personal access token instead. + + update-jupyterhub-dependencies: + # Don't run this job on forks + if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' + runs-on: ubuntu-20.04 + + # Write permissions granted for the peter-evans/create-pull-request action + # to push to a branch and create/update a PR + permissions: + contents: write + pull-requests: write + + steps: + - uses: actions/checkout@v3 + + - name: Get images/hub/requirements.in pinned version of jupyterhub + id: local + run: | + local_version=$(cat images/hub/requirements.in | grep 'jupyterhub==' | sed 's/jupyterhub==//') + echo "::set-output name=version::$local_version" + + - name: Get latest version of jupyterhub + id: latest + run: | + latest_version=$( + curl -s https://pypi.org/pypi/jupyterhub/json \ + | jq -r .info.version + ) + echo "::set-output name=version::$latest_version" + + - name: Update pinned version of jupyterhub + run: | + sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/hub/requirements.in + sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/hub/requirements.txt + sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt + sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml + + - name: git diff + run: git --no-pager diff --color=always + + # ref: https://github.com/peter-evans/create-pull-request + - name: Create a PR + uses: peter-evans/create-pull-request@v4.0.3 + with: + token: "${{ secrets.github_token }}" + branch: update-jupyterhub + labels: maintenance,dependencies + commit-message: Update jupyterhub version to ${{ steps.latest.outputs.version }} + title: Update jupyterhub version to ${{ steps.latest.outputs.version }} + body: >- + A new jupyterhub image version has been detected, version + `${{ steps.latest.outputs.version }}`. + + Please close and reopen this PR to run tests for now. This PR was opened with a `secrets.github_token` and will therefore not trigger other workflows to run. This can be resolved if we create a bot From b1dc25ec965f6945f203358f710f38bb52f5abe7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 15:42:20 +0200 Subject: [PATCH 285/898] ci: let jupyterhub version bump be the time we refreeze images/hub/requirements.txt --- .github/workflows/watch-dependencies.yaml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 06736ac8cc..2f4765a473 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -7,6 +7,10 @@ # - Watch the proxy.traefik.image.tag field of values.yaml to match the latest # traefik image tag. # +# - Watch the jupyterhub pinning in images/hub/requirements.in to match the +# latest jupyterhub version available on PyPI, and if doing this, also +# refreeze images/hub/requirements.txt. +# name: Watch dependencies on: @@ -64,13 +68,16 @@ jobs: echo "::set-output name=tag::$latest_tag" - name: Update values.yaml pinned tag + if: steps.local.outputs.tag != steps.latest.outputs.tag run: sed --in-place 's/${{ steps.local.outputs.tag }}/${{ steps.latest.outputs.tag }}/g' jupyterhub/values.yaml - name: git diff + if: steps.local.outputs.tag != steps.latest.outputs.tag run: git --no-pager diff --color=always # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR + if: steps.local.outputs.tag != steps.latest.outputs.tag uses: peter-evans/create-pull-request@v4.0.3 with: token: "${{ secrets.github_token }}" @@ -101,6 +108,7 @@ jobs: steps: - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 - name: Get images/hub/requirements.in pinned version of jupyterhub id: local @@ -118,17 +126,28 @@ jobs: echo "::set-output name=version::$latest_version" - name: Update pinned version of jupyterhub + if: steps.local.outputs.version != steps.latest.outputs.version run: | sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/hub/requirements.in - sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/hub/requirements.txt sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml + - name: Install dependencies for images/hub/dependencies script + run: pip install click ruamel.yaml + + - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in + if: steps.local.outputs.version != steps.latest.outputs.version + run: | + cd images/hub + ./dependencies freeze --upgrade + - name: git diff + if: steps.local.outputs.version != steps.latest.outputs.version run: git --no-pager diff --color=always # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR + if: steps.local.outputs.version != steps.latest.outputs.version uses: peter-evans/create-pull-request@v4.0.3 with: token: "${{ secrets.github_token }}" From d30f34b13cb7e94d4242eab1de16a473aba77b5b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 15:55:59 +0200 Subject: [PATCH 286/898] ci, dependencies script: remove -it, add --user=root, flags as key=value - With -it, we get issues starting the docker container as it will try to map the non-existing stdin to the docker containers stdin - or something like this. - Without --user=root, we get permissions errors in GitHub Actions when accessing requirements.txt for writing. --- .github/workflows/watch-dependencies.yaml | 5 ++--- images/hub/dependencies | 14 +++++--------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 2f4765a473..4ccdc5c8ca 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -14,6 +14,7 @@ name: Watch dependencies on: + push: schedule: # Run at 05:00 every day, ref: https://crontab.guru/#0_5_*_*_* - cron: "0 5 * * *" @@ -137,9 +138,7 @@ jobs: - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in if: steps.local.outputs.version != steps.latest.outputs.version - run: | - cd images/hub - ./dependencies freeze --upgrade + run: images/hub/dependencies freeze --upgrade - name: git diff if: steps.local.outputs.version != steps.latest.outputs.version diff --git a/images/hub/dependencies b/images/hub/dependencies index ade4ed93d8..d3166ec12e 100755 --- a/images/hub/dependencies +++ b/images/hub/dependencies @@ -32,7 +32,7 @@ here = os.path.dirname(os.path.abspath(__file__)) chartpress_yaml = os.path.join(here, os.pardir, os.pardir, "chartpress.yaml") values_yaml = os.path.join(here, os.pardir, os.pardir, "jupyterhub", "values.yaml") dependencies_image = "hub-dependencies" -pip_tools_version = "5.*" +pip_tools_version = "6.*" @lru_cache() @@ -117,13 +117,10 @@ def freeze(build, upgrade, upgrade_package): "docker", "run", "--rm", - "-it", - "-e", - "CUSTOM_COMPILE_COMMAND=./dependencies freeze --upgrade", - "--volume", - f"{here}:/io", - "--workdir", - "/io", + "--env=CUSTOM_COMPILE_COMMAND=./dependencies freeze --upgrade", + "--user=root", + f"--volume={here}:/io", + "--workdir=/io", dependencies_image, "pip-compile", ] @@ -153,7 +150,6 @@ def outdated(build): "docker", "run", "--rm", - "-it", dependencies_image, "pip", "list", From 9ffcab61ee31ca44b4c8e56c9ea62d5d6c9fae11 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 18:40:34 +0200 Subject: [PATCH 287/898] docs: how to adjust profile_list dynamically based on user etc --- docs/source/jupyterhub/customizing/user-environment.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 5eefc170d5..5f4cf87017 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -432,10 +432,10 @@ of configuration options that override your JupyterHub's default configuration Docker images, to select the hardware on which they want their jobs to run, or to configure default interfaces such as Jupyter Lab vs. RStudio. -Each configuration is a set of options for [Kubespawner](https://github.com/jupyterhub/kubespawner), +Each configuration is a set of options for [KubeSpawner](https://github.com/jupyterhub/kubespawner), which defines how Kubernetes should launch a new user server pod. Any configuration options passed to the `profileList` configuration will -overwrite the defaults in Kubespawner (or any configuration you've +overwrite the defaults in KubeSpawner (or any configuration you've added elsewhere in your helm chart). Profiles are stored under `singleuser.profileList`, and are defined as @@ -501,6 +501,10 @@ hooks in `kubespawner_override`, the configuration is for `lifecycle_hooks` (sna how it is used directly under the `singleuser` configuration section. [A further explanation for this can be found in this github issue.](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/1242#issuecomment-484895216) +```{note} +It is also possible to configure the profile choices presented to the user depending on the user. For now, this is documented in [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449). +``` + ```{note} You can also **control the HTML used for the profile selection page** by using the Kubespawner `profile_form_template` configuration. See the From 9e04c1330e36a86c7d53091bb381b5114347ee73 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 21:54:08 +0200 Subject: [PATCH 288/898] Revert "Improve documentation for singleuser.fsGid" This reverts commit 722e5b4bae0bd26840b72eb27989a173c3069413. --- jupyterhub/schema.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 76edfeb54b..ab1562cac3 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2020,11 +2020,7 @@ properties: description: *kubespawner-native-config-description fsGid: type: [integer, "null"] - description: | - The GID of the group that should own any volumes that are created and mounted. - You’ll have to set this if you are using auto-provisioned volumes with most cloud providers. - See `fs_gid` in the [KubeSpawner - documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html). + description: *kubespawner-native-config-description lifecycleHooks: type: object additionalProperties: false From fdb684aedbe561effee3ed7e15ff3eb96afa4cb6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 16 May 2022 22:09:44 +0200 Subject: [PATCH 289/898] docs: explicit links for kubespawner passthrough config --- jupyterhub/schema.yaml | 59 ++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ab1562cac3..59fa9fe14f 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1788,10 +1788,9 @@ properties: networkPolicy: *networkPolicy-spec podNameTemplate: type: [string, "null"] - description: &kubespawner-native-config-description | - KubeSpawner native configuration, see the [KubeSpawner - documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) - for more information. + description: | + Passthrough configuration for + [KubeSpawner.pod_name_template](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.pod_name_template). cpu: type: object additionalProperties: false @@ -1979,31 +1978,45 @@ properties: cmd: type: [array, string, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.cmd](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.cmd). defaultUrl: type: [string, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.default_url](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.default_url). # FIXME: name mismatch, named events_enabled in kubespawner events: type: [boolean, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.events_enabled](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.events_enabled). extraAnnotations: type: object additionalProperties: false patternProperties: *labels-and-annotations-patternProperties - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_annotations](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_annotations). extraContainers: type: array - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_containers](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_containers). extraLabels: type: object additionalProperties: false patternProperties: *labels-and-annotations-patternProperties - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_labels](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_labels). extraPodConfig: type: object additionalProperties: true - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_pod_config](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_pod_config). extraResource: type: object additionalProperties: false @@ -2012,19 +2025,27 @@ properties: guarantees: type: object additionalProperties: true - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_resource_guarantees](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_resource_guarantees). # FIXME: name mismatch, named extra_resource_limits in kubespawner limits: type: object additionalProperties: true - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.extra_resource_limits](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.extra_resource_limits). fsGid: type: [integer, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.fs_gid](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.fs_gid). lifecycleHooks: type: object additionalProperties: false - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.lifecycle_hooks](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.lifecycle_hooks). properties: postStart: type: object @@ -2049,10 +2070,14 @@ properties: # FIXME: name mismatch, named service_account in kubespawner serviceAccountName: type: [string, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.service_account](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.service_account). startTimeout: type: [integer, "null"] - description: *kubespawner-native-config-description + description: | + Passthrough configuration for + [KubeSpawner.start_timeout](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.start_timeout). storage: type: object additionalProperties: false From 4db0ecc3c4d03b70d911d45ce042fe6ab66af442 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 May 2022 09:20:48 +0200 Subject: [PATCH 290/898] ci: automatically bump kube-scheduler and pause image tags --- .github/workflows/watch-dependencies.yaml | 24 +++++++++++++++----- jupyterhub/values.yaml | 27 +++++++++++++++++------ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 4ccdc5c8ca..fe2ab5acf2 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -40,10 +40,22 @@ jobs: registry: registry.hub.docker.com repository: jupyterhub/configurable-http-proxy values_path: proxy.chp.image.tag + version_startswith: "" - name: traefik registry: registry.hub.docker.com repository: library/traefik values_path: proxy.traefik.image.tag + version_startswith: "" + - name: kube-scheduler + registry: k8s.gcr.io + repository: kube-scheduler + values_path: scheduling.userScheduler.image.tag + version_startswith: "v1.23" + - name: pause + registry: k8s.gcr.io + repository: pause + values_path: scheduling.userPlaceholder.image.tag + version_startswith: "" steps: - uses: actions/checkout@v3 @@ -57,20 +69,22 @@ jobs: - name: Get latest tag of ${{ matrix.registry }}/${{ matrix.repository }} id: latest # The skopeo image helps us list tags consistently from different docker - # registries. We use jq to filter out tags of the x.y.z format with the - # optional v prefix, and then sort based on the numerical x, y, and z - # values. Finally, we pick the last value in the list. + # registries. We use jq to filter out tags of the x.y or x.y.z format + # with the optional v prefix or version_startswith filter, and then sort + # based on the numerical x, y, and z values. Finally, we pick the last + # value in the list. # run: | latest_tag=$( docker run --rm quay.io/skopeo/stable list-tags docker://${{ matrix.registry }}/${{ matrix.repository }} \ - | jq -r '[.Tags[] | select(. | test("^v?\\d+\\.\\d+\\.\\d+$"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' + | jq -r '[.Tags[] | select(. | match("^v?\\d+\\.\\d+(\\.\\d+)?$") | .string | startswith("${{ matrix.version_startswith }}"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' ) echo "::set-output name=tag::$latest_tag" - name: Update values.yaml pinned tag if: steps.local.outputs.tag != steps.latest.outputs.tag - run: sed --in-place 's/${{ steps.local.outputs.tag }}/${{ steps.latest.outputs.tag }}/g' jupyterhub/values.yaml + run: | + sed --in-place 's/tag: "${{ steps.local.outputs.tag }}"/tag: "${{ steps.latest.outputs.tag }}"/g' jupyterhub/values.yaml - name: git diff if: steps.local.outputs.tag != steps.latest.outputs.tag diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5388c6b296..7a5837e283 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -194,7 +194,10 @@ proxy: allowPrivilegeEscalation: false image: name: jupyterhub/configurable-http-proxy - tag: 4.5.1 # https://github.com/jupyterhub/configurable-http-proxy/releases + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "4.5.1" # https://github.com/jupyterhub/configurable-http-proxy/releases pullPolicy: pullSecrets: [] extraCommandLineFlags: [] @@ -239,7 +242,10 @@ proxy: allowPrivilegeEscalation: false image: name: traefik - tag: v2.6.6 # ref: https://hub.docker.com/_/traefik?tab=tags + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "v2.6.6" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: @@ -490,7 +496,12 @@ scheduling: # is a good sign kube-scheduler is ready to schedule pods. # name: k8s.gcr.io/kube-scheduler - tag: v1.23.4 # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. The minor version is pinned in the + # workflow, and should be updated there if a minor version bump is done + # here. + # + tag: "v1.23.4" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} @@ -513,10 +524,11 @@ scheduling: enabled: true image: name: k8s.gcr.io/pause - # tag's can be updated by inspecting the output of the command: - # gcloud container images list-tags k8s.gcr.io/pause --sort-by=~tags + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. # # If you update this, also update prePuller.pause.image.tag + # tag: "3.6" pullPolicy: pullSecrets: [] @@ -591,10 +603,11 @@ prePuller: allowPrivilegeEscalation: false image: name: k8s.gcr.io/pause - # tag's can be updated by inspecting the output of the command: - # gcloud container images list-tags k8s.gcr.io/pause --sort-by=~tags + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. # # If you update this, also update scheduling.userPlaceholder.image.tag + # tag: "3.6" pullPolicy: pullSecrets: [] From 637d4c7f6e8b06d13199d5ec4aa45ed6c3210dfa Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 May 2022 09:26:30 +0200 Subject: [PATCH 291/898] ci: include old version in PR titles bumping versions --- .github/workflows/watch-dependencies.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index fe2ab5acf2..f12a99975b 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -98,8 +98,8 @@ jobs: token: "${{ secrets.github_token }}" branch: update-image-${{ matrix.name }} labels: maintenance,dependencies - commit-message: Update ${{ matrix.repository }} version to ${{ steps.latest.outputs.tag }} - title: Update ${{ matrix.repository }} version to ${{ steps.latest.outputs.tag }} + commit-message: Update ${{ matrix.repository }} version from ${{ steps.local.outputs.tag }} to ${{ steps.latest.outputs.tag }} + title: Update ${{ matrix.repository }} version from ${{ steps.local.outputs.tag }} to ${{ steps.latest.outputs.tag }} body: >- A new ${{ matrix.repository }} image version has been detected, version `${{ steps.latest.outputs.tag }}`. @@ -166,10 +166,10 @@ jobs: token: "${{ secrets.github_token }}" branch: update-jupyterhub labels: maintenance,dependencies - commit-message: Update jupyterhub version to ${{ steps.latest.outputs.version }} - title: Update jupyterhub version to ${{ steps.latest.outputs.version }} + commit-message: Update jupyterhub from ${{ steps.local.outputs.version }} to ${{ steps.latest.outputs.version }} + title: Update jupyterhub from ${{ steps.local.outputs.version }} to ${{ steps.latest.outputs.version }} body: >- - A new jupyterhub image version has been detected, version + A new jupyterhub version has been detected, version `${{ steps.latest.outputs.version }}`. From 51260074fc80d5c852e1a2c63240f5ebb4d0c7ff Mon Sep 17 00:00:00 2001 From: consideRatio Date: Tue, 17 May 2022 08:09:21 +0000 Subject: [PATCH 292/898] Update kube-scheduler version from v1.23.4 to v1.23.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7a5837e283..dff6a10202 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -501,7 +501,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.4" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.6" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From d057fe9d380e7740b9c78836090c3ee1460a1d39 Mon Sep 17 00:00:00 2001 From: consideRatio Date: Tue, 17 May 2022 08:09:22 +0000 Subject: [PATCH 293/898] Update pause version from 3.6 to 3.7 --- jupyterhub/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7a5837e283..856b43a464 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -529,7 +529,7 @@ scheduling: # # If you update this, also update prePuller.pause.image.tag # - tag: "3.6" + tag: "3.7" pullPolicy: pullSecrets: [] replicas: 0 @@ -608,7 +608,7 @@ prePuller: # # If you update this, also update scheduling.userPlaceholder.image.tag # - tag: "3.6" + tag: "3.7" pullPolicy: pullSecrets: [] From cab2deda0a1508978d60029a36d3de984bfba711 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 May 2022 10:45:13 +0200 Subject: [PATCH 294/898] ci: update comment to reflect more images are bumped --- .github/workflows/watch-dependencies.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index f12a99975b..e958723be5 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -1,11 +1,8 @@ # This is a GitHub workflow defining a set of jobs with a set of steps. # ref: https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions # -# - Watch the proxy.chp.image.tag field of values.yaml to match the latest -# jupyterhub/configurable-http-proxy image tag. -# -# - Watch the proxy.traefik.image.tag field of values.yaml to match the latest -# traefik image tag. +# - Watch multiple images tags referenced in values.yaml to match the latest +# image tag. # # - Watch the jupyterhub pinning in images/hub/requirements.in to match the # latest jupyterhub version available on PyPI, and if doing this, also From d40aac7a829851b093f0572f1cff718885f62d8d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 May 2022 10:45:34 +0200 Subject: [PATCH 295/898] ci: don't run watch-dependencies.yaml workflow on pushes in general --- .github/workflows/watch-dependencies.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index e958723be5..996da8f449 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -12,6 +12,8 @@ name: Watch dependencies on: push: + paths: + - ".github/workflows/watch-dependencies.yaml" schedule: # Run at 05:00 every day, ref: https://crontab.guru/#0_5_*_*_* - cron: "0 5 * * *" From b6e23b34e90276f6d5a7357907c89add4e79f5ea Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 17 May 2022 11:06:32 +0200 Subject: [PATCH 296/898] ci: force traefik to use x.y.z tags over the unstable x.y tags --- .github/workflows/watch-dependencies.yaml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 996da8f449..74a0997b3c 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -40,21 +40,39 @@ jobs: repository: jupyterhub/configurable-http-proxy values_path: proxy.chp.image.tag version_startswith: "" + version_patch_regexp_group_suffix: "" + + # traefik made us need to introduce version_patch_regexp_group_suffix + # to control if we accept omitting the patch version. The traefik + # image provides tags like 2.7 even though 2.7.0 hasn't been released, + # which makes bumping to it something we don't want to do. We still + # need to accept major.minor formatted tags though as the pause image + # has them. + # - name: traefik registry: registry.hub.docker.com repository: library/traefik values_path: proxy.traefik.image.tag version_startswith: "" + version_patch_regexp_group_suffix: "" + + # kube-scheduler should be pinned to its minor version as bumping it + # will require manual interventions sometimes, see the notes in + # values.yaml about bumping its version. + # - name: kube-scheduler registry: k8s.gcr.io repository: kube-scheduler values_path: scheduling.userScheduler.image.tag version_startswith: "v1.23" + version_patch_regexp_group_suffix: "" + - name: pause registry: k8s.gcr.io repository: pause values_path: scheduling.userPlaceholder.image.tag version_startswith: "" + version_patch_regexp_group_suffix: "?" steps: - uses: actions/checkout@v3 @@ -76,7 +94,7 @@ jobs: run: | latest_tag=$( docker run --rm quay.io/skopeo/stable list-tags docker://${{ matrix.registry }}/${{ matrix.repository }} \ - | jq -r '[.Tags[] | select(. | match("^v?\\d+\\.\\d+(\\.\\d+)?$") | .string | startswith("${{ matrix.version_startswith }}"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' + | jq -r '[.Tags[] | select(. | match("^v?\\d+\\.\\d+(\\.\\d+)${{ matrix.version_patch_regexp_group_suffix }}$") | .string | startswith("${{ matrix.version_startswith }}"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' ) echo "::set-output name=tag::$latest_tag" From cd805416526575dbd51b057c19ffd8e1d5af96f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 May 2022 00:05:14 +0000 Subject: [PATCH 297/898] build(deps): bump py-spy from 0.3.11 to 0.3.12 in /images/hub Bumps [py-spy](https://github.com/benfred/py-spy) from 0.3.11 to 0.3.12. - [Release notes](https://github.com/benfred/py-spy/releases) - [Changelog](https://github.com/benfred/py-spy/blob/master/CHANGELOG.md) - [Commits](https://github.com/benfred/py-spy/compare/v0.3.11...v0.3.12) --- updated-dependencies: - dependency-name: py-spy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 46 +++++++++++++++---------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index dca59f75ee..0e2171a15f 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -61,12 +61,6 @@ idna==3.3 # via # requests # yarl -importlib-metadata==4.11.3 - # via alembic -importlib-resources==5.7.1 - # via - # alembic - # jsonschema jinja2==3.1.2 # via # jupyterhub @@ -77,6 +71,16 @@ jsonschema==4.5.1 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub +jupyterhub==2.3.0 + # via + # -r requirements.in + # jupyterhub-firstuseauthenticator + # jupyterhub-kubespawner + # jupyterhub-ldapauthenticator + # jupyterhub-ltiauthenticator + # jupyterhub-nativeauthenticator + # nullauthenticator + # oauthenticator jupyterhub-firstuseauthenticator==1.0.0 # via -r requirements.in jupyterhub-hmacauthenticator==1.0 @@ -93,16 +97,6 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jupyterhub==2.3.0 - # via - # -r requirements.in - # jupyterhub-firstuseauthenticator - # jupyterhub-kubespawner - # jupyterhub-ldapauthenticator - # jupyterhub-ltiauthenticator - # jupyterhub-nativeauthenticator - # nullauthenticator - # oauthenticator jwcrypto==1.2 # via jupyterhub-ltiauthenticator kubernetes-asyncio==22.6.4 @@ -145,7 +139,7 @@ prometheus-client==0.14.1 # via jupyterhub psycopg2-binary==2.9.3 # via -r requirements.in -py-spy==0.3.11 +py-spy==0.3.12 # via -r requirements.in pyasn1==0.4.8 # via ldap3 @@ -187,18 +181,18 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests-oauthlib==1.3.1 - # via mwoauth requests==2.27.1 # via # jupyterhub # mwoauth # pyjwkest # requests-oauthlib -ruamel.yaml.clib==0.2.6 - # via ruamel.yaml -ruamel.yaml==0.17.21 +requests-oauthlib==1.3.1 + # via mwoauth +ruamel-yaml==0.17.21 # via jupyter-telemetry +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml six==1.16.0 # via # kubernetes-asyncio @@ -206,13 +200,13 @@ six==1.16.0 # onetimepass # pyjwkest # python-dateutil -sqlalchemy-cockroachdb==1.4.3 - # via -r requirements.in sqlalchemy==1.4.36 # via # alembic # jupyterhub # sqlalchemy-cockroachdb +sqlalchemy-cockroachdb==1.4.3 + # via -r requirements.in statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 @@ -236,10 +230,6 @@ wrapt==1.14.1 # via deprecated yarl==1.7.2 # via aiohttp -zipp==3.8.0 - # via - # importlib-metadata - # importlib-resources # The following packages are considered to be unsafe in a requirements file: # setuptools From 169f74f8d489887a9a8d163c12e3352ed468b452 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 18 May 2022 09:15:20 +0200 Subject: [PATCH 298/898] ci: fix syntax error in dependabot config --- .github/dependabot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 6491329d5d..5d6f51621f 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -35,7 +35,7 @@ updates: # job in the watch-dependencies.yaml workflow file. # ignore: - - jupyterhub + - dependency-name: jupyterhub labels: - maintenance - dependencies From cf1f0df0b3c3d59e7a64ee5f276d9cad6e7df283 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 18 May 2022 15:05:28 +0200 Subject: [PATCH 299/898] ci: use jupyterhub-bot PAT to trigger github workflow on opened PRs --- .github/workflows/vuln-scan.yaml | 7 +----- .github/workflows/watch-dependencies.yaml | 28 ++--------------------- 2 files changed, 3 insertions(+), 32 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 493a095c42..7a629d043d 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -21,11 +21,6 @@ jobs: trivy_image_scan: if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 - # Write permissions granted for the peter-evans/create-pull-request action - # to push to a branch and create/update a PR - permissions: - contents: write - pull-requests: write strategy: fail-fast: false @@ -197,7 +192,7 @@ jobs: if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 with: - token: "${{ secrets.GITHUB_TOKEN }}" + token: "${{ secrets.jupyterhub_bot_pat }}" author: jupyterhub vuln-scan bot reviewers: consideratio branch: vuln-scan-${{ matrix.image_ref }} diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 74a0997b3c..d717e72e3c 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -25,12 +25,6 @@ jobs: if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 - # Write permissions granted for the peter-evans/create-pull-request action - # to push to a branch and create/update a PR - permissions: - contents: write - pull-requests: write - strategy: fail-fast: false matrix: @@ -112,7 +106,7 @@ jobs: if: steps.local.outputs.tag != steps.latest.outputs.tag uses: peter-evans/create-pull-request@v4.0.3 with: - token: "${{ secrets.github_token }}" + token: "${{ secrets.jupyterhub_bot_pat }}" branch: update-image-${{ matrix.name }} labels: maintenance,dependencies commit-message: Update ${{ matrix.repository }} version from ${{ steps.local.outputs.tag }} to ${{ steps.latest.outputs.tag }} @@ -121,23 +115,11 @@ jobs: A new ${{ matrix.repository }} image version has been detected, version `${{ steps.latest.outputs.tag }}`. - - Please close and reopen this PR to run tests for now. This PR was - opened with a `secrets.github_token` and will therefore not trigger - other workflows to run. This can be resolved if we create a bot - account and use its personal access token instead. - update-jupyterhub-dependencies: # Don't run this job on forks if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 - # Write permissions granted for the peter-evans/create-pull-request action - # to push to a branch and create/update a PR - permissions: - contents: write - pull-requests: write - steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 @@ -180,7 +162,7 @@ jobs: if: steps.local.outputs.version != steps.latest.outputs.version uses: peter-evans/create-pull-request@v4.0.3 with: - token: "${{ secrets.github_token }}" + token: "${{ secrets.jupyterhub_bot_pat }}" branch: update-jupyterhub labels: maintenance,dependencies commit-message: Update jupyterhub from ${{ steps.local.outputs.version }} to ${{ steps.latest.outputs.version }} @@ -188,9 +170,3 @@ jobs: body: >- A new jupyterhub version has been detected, version `${{ steps.latest.outputs.version }}`. - - - Please close and reopen this PR to run tests for now. This PR was - opened with a `secrets.github_token` and will therefore not trigger - other workflows to run. This can be resolved if we create a bot - account and use its personal access token instead. From f68e2734348c60db7cae1bb33436e564767a5d3e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 18 May 2022 15:37:22 +0200 Subject: [PATCH 300/898] ci: don't trigger 2x tests on bump automation PRs --- .github/workflows/publish.yml | 2 ++ .github/workflows/test-chart.yaml | 2 ++ .github/workflows/test-docker-build.yaml | 2 ++ .github/workflows/test-docs.yaml | 2 ++ 4 files changed, 8 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c46ad6b7c3..cdf0569bd0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,6 +20,8 @@ on: branches-ignore: - "dependabot/**" - "pre-commit-ci-update-config" + - "update-*" + - "vuln-scan-*" tags: - "**" diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 5afbbff96d..8fa4f56a3c 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -22,6 +22,8 @@ on: branches-ignore: - "dependabot/**" - "pre-commit-ci-update-config" + - "update-*" + - "vuln-scan-*" workflow_dispatch: jobs: diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index ae98e25ad4..c32a933934 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -20,6 +20,8 @@ on: branches-ignore: - "dependabot/**" - "pre-commit-ci-update-config" + - "update-*" + - "vuln-scan-*" workflow_dispatch: jobs: diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index ae30c63891..fee7b362ae 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -19,6 +19,8 @@ on: branches-ignore: - "dependabot/**" - "pre-commit-ci-update-config" + - "update-*" + - "vuln-scan-*" workflow_dispatch: jobs: From ecba49970170ac323334bd385dc63a556e87dc22 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 18 May 2022 15:46:09 +0200 Subject: [PATCH 301/898] ci: update commit author/committer for automatic pr creation --- .github/workflows/vuln-scan.yaml | 3 ++- .github/workflows/watch-dependencies.yaml | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 7a629d043d..656deebf44 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -193,7 +193,8 @@ jobs: uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 with: token: "${{ secrets.jupyterhub_bot_pat }}" - author: jupyterhub vuln-scan bot + author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> + committer: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> reviewers: consideratio branch: vuln-scan-${{ matrix.image_ref }} title: Vulnerability patch in ${{ matrix.image_ref }} diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index d717e72e3c..bd649eb543 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -107,6 +107,8 @@ jobs: uses: peter-evans/create-pull-request@v4.0.3 with: token: "${{ secrets.jupyterhub_bot_pat }}" + author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> + committer: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> branch: update-image-${{ matrix.name }} labels: maintenance,dependencies commit-message: Update ${{ matrix.repository }} version from ${{ steps.local.outputs.tag }} to ${{ steps.latest.outputs.tag }} @@ -163,6 +165,8 @@ jobs: uses: peter-evans/create-pull-request@v4.0.3 with: token: "${{ secrets.jupyterhub_bot_pat }}" + author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> + committer: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> branch: update-jupyterhub labels: maintenance,dependencies commit-message: Update jupyterhub from ${{ steps.local.outputs.version }} to ${{ steps.latest.outputs.version }} From 3e943bf47383a04e6f6566cd316275d614fff022 Mon Sep 17 00:00:00 2001 From: Seth Nickell Date: Mon, 11 Apr 2022 19:23:50 -1000 Subject: [PATCH 302/898] kubespawner: pass "allow_privilege_escalation" --- jupyterhub/files/hub/jupyterhub_config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 9f3d01a8e6..12af8106d7 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -166,6 +166,7 @@ def camelCaseify(s): ("environment", "extraEnv"), ("profile_list", None), ("extra_pod_config", None), + ("allow_privilege_escalation", None), ): if cfg_key is None: cfg_key = camelCaseify(trait) From 95c6f0a2ca60bd99eff6a0dcae05a3571d0a189f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 18 May 2022 19:50:16 +0200 Subject: [PATCH 303/898] Add singleuser.allowPrivilegeEscalation --- jupyterhub/files/hub/jupyterhub_config.py | 11 ++++++++++- jupyterhub/schema.yaml | 8 ++++++++ jupyterhub/values.yaml | 1 + tools/templates/lint-and-validate-values.yaml | 1 + 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 12af8106d7..d3f9ffb3c5 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -142,6 +142,7 @@ def camelCaseify(s): ("events_enabled", "events"), ("extra_labels", None), ("extra_annotations", None), + # ("allow_privilege_escalation", None), # Managed manually below ("uid", None), ("fs_gid", None), ("service_account", "serviceAccountName"), @@ -166,7 +167,6 @@ def camelCaseify(s): ("environment", "extraEnv"), ("profile_list", None), ("extra_pod_config", None), - ("allow_privilege_escalation", None), ): if cfg_key is None: cfg_key = camelCaseify(trait) @@ -180,6 +180,15 @@ def camelCaseify(s): c.KubeSpawner.image = image +# allow_privilege_escalation defaults to False in KubeSpawner 2+. Since its a +# property where None, False, and True all are valid values that users of the +# Helm chart may want to set, we can't use the set_config_if_not_none helper +# function as someone may want to override the default False value to None. +# +c.KubeSpawner.allow_privilege_escalation = get_config( + "singleuser.allowPrivilegeEscalation" +) + # Combine imagePullSecret.create (single), imagePullSecrets (list), and # singleuser.image.pullSecrets (list). image_pull_secrets = [] diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index f0036cc225..7c8fec45a6 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2185,9 +2185,17 @@ properties: Decide if you want storage to be provisioned dynamically (dynamic), or if you want to attach existing storage (static), or don't want any storage to be attached (none). + allowPrivilegeEscalation: + type: [boolean, "null"] + description: | + Passthrough configuration for + [KubeSpawner.allow_privilege_escalation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.allow_privilege_escalation). uid: type: [integer, "null"] description: | + Passthrough configuration for + [KubeSpawner.uid](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.uid). + This dictates as what user the main container will start up as. As an example of when this is needed, consider if you want to enable diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index e382f24352..d8fce8d527 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -361,6 +361,7 @@ singleuser: lifecycleHooks: {} initContainers: [] extraContainers: [] + allowPrivilegeEscalation: false uid: 1000 fsGid: 100 serviceAccountName: diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 7671bab64a..7f5d641e5f 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -397,6 +397,7 @@ singleuser: - name: mock-extra-container-name image: mock-extra-container-image imagePullPolicy: IfNotPresent + allowPrivilegeEscalation: false uid: 1000 fsGid: 100 serviceAccountName: From 27ee01d01a2d29ce92d1829a943c336be860bc52 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 19 May 2022 14:02:09 +0200 Subject: [PATCH 304/898] Update docs/source/jupyterhub/customizing/user-environment.md Co-authored-by: Chris Holdgraf --- docs/source/jupyterhub/customizing/user-environment.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 5f4cf87017..18419d38b3 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -501,10 +501,11 @@ hooks in `kubespawner_override`, the configuration is for `lifecycle_hooks` (sna how it is used directly under the `singleuser` configuration section. [A further explanation for this can be found in this github issue.](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/1242#issuecomment-484895216) -```{note} -It is also possible to configure the profile choices presented to the user depending on the user. For now, this is documented in [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449). -``` +### User-dependent profile options +It is also possible to configure the profile choices presented to the user depending on the user. +You can do this by defining a custom **pre-spawn hook** that populates the profile list based on user identity. +See [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449) for some examples of how this works. ```{note} You can also **control the HTML used for the profile selection page** by using the Kubespawner `profile_form_template` configuration. See the From 498a36c5d3254e42907ed5635673e814574dcccb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 May 2022 12:02:20 +0000 Subject: [PATCH 305/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/jupyterhub/customizing/user-environment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 18419d38b3..84c0fbd655 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -506,6 +506,7 @@ how it is used directly under the `singleuser` configuration section. It is also possible to configure the profile choices presented to the user depending on the user. You can do this by defining a custom **pre-spawn hook** that populates the profile list based on user identity. See [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449) for some examples of how this works. + ```{note} You can also **control the HTML used for the profile selection page** by using the Kubespawner `profile_form_template` configuration. See the From 950fd2cc1090ebc9754768faca2376c193abce6b Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Thu, 19 May 2022 05:03:26 -0700 Subject: [PATCH 306/898] Update docs/source/jupyterhub/customizing/user-environment.md --- docs/source/jupyterhub/customizing/user-environment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 84c0fbd655..cbac0f675d 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -507,6 +507,7 @@ It is also possible to configure the profile choices presented to the user depen You can do this by defining a custom **pre-spawn hook** that populates the profile list based on user identity. See [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449) for some examples of how this works. + ```{note} You can also **control the HTML used for the profile selection page** by using the Kubespawner `profile_form_template` configuration. See the From 42361f0e73221e1c07281d6b5da6fa9be5ff1fa0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 May 2022 12:03:38 +0000 Subject: [PATCH 307/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/jupyterhub/customizing/user-environment.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index cbac0f675d..84c0fbd655 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -507,7 +507,6 @@ It is also possible to configure the profile choices presented to the user depen You can do this by defining a custom **pre-spawn hook** that populates the profile list based on user identity. See [this discourse post](https://discourse.jupyter.org/t/tailoring-spawn-options-and-server-configuration-to-certain-users/8449) for some examples of how this works. - ```{note} You can also **control the HTML used for the profile selection page** by using the Kubespawner `profile_form_template` configuration. See the From 1cc6ee8909d73d4b63bdf8540dc673900bcb35fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 May 2022 05:05:44 +0000 Subject: [PATCH 308/898] build(deps): bump jupyterhub-kubespawner in /images/hub Bumps [jupyterhub-kubespawner](https://github.com/jupyterhub/kubespawner) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/jupyterhub/kubespawner/releases) - [Changelog](https://github.com/jupyterhub/kubespawner/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/kubespawner/compare/4.0.0...4.1.0) --- updated-dependencies: - dependency-name: jupyterhub-kubespawner dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 0e2171a15f..84ea7a5da5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -87,7 +87,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==4.0.0 +jupyterhub-kubespawner==4.1.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in From a500aa4c15554bc089220c755f903597330718d3 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 22 May 2022 19:21:45 +0100 Subject: [PATCH 309/898] Add opengraph tags --- docs/requirements.txt | 1 + docs/source/conf.py | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 04a5387a83..82adf09e9b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -13,4 +13,5 @@ pydata-sphinx-theme pyyaml sphinx-autobuild sphinx-copybutton +sphinxext-opengraph sphinxext-rediraffe diff --git a/docs/source/conf.py b/docs/source/conf.py index 6b225382a7..aad2193211 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -102,6 +102,7 @@ def _get_git_ref_from_chartpress_based_version(version): "sphinx_copybutton", "myst_parser", "sphinxext.rediraffe", + "sphinxext.opengraph", ] # List of patterns, relative to source directory, that match files and From 5470e5736b78edd991f39f90a7889ed60a959450 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 23 May 2022 09:41:10 +0200 Subject: [PATCH 310/898] maint: remove pre-v1 deprecation logic pending v2 release --- jupyterhub/templates/NOTES.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index abb0836711..7130728fd1 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -110,18 +110,6 @@ {{- end }} -{{- if eq .Values.proxy.https.enabled false }} -{{- if or (not (eq .Values.proxy.https.type "letsencrypt")) (not (eq (.Values.proxy.https.letsencrypt.contactEmail | default "") "")) }} -################################################################################# -###### WARNING: proxy.https.enabled is set to false by default since ##### -###### version 0.10.0. It is now set to false but proxy.https ##### -###### has been modified indicating you may want it enabled. ##### -################################################################################# -{{- println }} -{{- end }} -{{- end }} - - From 805f8afc4c1f7540f5946383153ca16061d61932 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 23 May 2022 11:40:41 +0200 Subject: [PATCH 311/898] breaking: add hub.podSecurityContext, remove hub.fsGid --- jupyterhub/schema.yaml | 23 +++++++++++++++-------- jupyterhub/templates/NOTES.txt | 5 +++++ jupyterhub/templates/hub/deployment.yaml | 4 +++- jupyterhub/values.yaml | 3 ++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 7c8fec45a6..c13067412e 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -873,14 +873,15 @@ properties: fsGid: type: [integer, "null"] minimum: 0 - description: - The gid the hub process should be using when touching any volumes mounted. - - Use this only if you are building your own image & know that a group with this gid - exists inside the hub container! Advanced feature, handle with care! - - Defaults to 1000, which is the gid of the `jovyan` user that is present in the - default hub image. + # This schema entry is needed to help us print a more helpful error + # message in NOTES.txt if hub.fsGid is set. + # + description: | + ```{note} + Removed in version 2.0.0. Use + [`hub.podSecurityContext`](schema_hub.podSecurityContext) and specify + `fsGroup` instead. + ``` service: type: object additionalProperties: false @@ -1035,6 +1036,12 @@ properties: consecutiveFailureLimit: type: [integer, "null"] description: *jupyterhub-native-config-description + podSecurityContext: &podSecurityContext-spec + additionalProperties: true + description: | + A k8s native specification of the pod's security context, see [the + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core) + for details. containerSecurityContext: &containerSecurityContext-spec type: object additionalProperties: true diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index abb0836711..9b935d411b 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -150,6 +150,11 @@ */}} +{{- if hasKey .Values.hub "fsGid" }} +{{- $breaking = print $breaking "\n\nCHANGED: hub.fsGid must as of version 2.0.0 be configured via hub.podSecurityContext.fsGroup." }} +{{- end }} + + {{- if $breaking }} {{- fail (print $breaking_title $breaking "\n\n") }} {{- end }} diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index 2060b97732..321cd88640 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -78,8 +78,10 @@ spec: {{- if .Values.rbac.enabled }} serviceAccountName: {{ include "jupyterhub.hub.fullname" . }} {{- end }} + {{- with .Values.hub.podSecurityContext }} securityContext: - fsGroup: {{ .Values.hub.fsGid }} + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.hub.image) }} imagePullSecrets: {{ . }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index d8fce8d527..d6b5775cd9 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -40,7 +40,6 @@ hub: baseUrl: / cookieSecret: initContainers: [] - fsGid: 1000 nodeSelector: {} tolerations: [] concurrentSpawnLimit: 64 @@ -83,6 +82,8 @@ hub: pullPolicy: pullSecrets: [] resources: {} + podSecurityContext: + fsGroup: 1000 containerSecurityContext: runAsUser: 1000 runAsGroup: 1000 From 13d97a94ca28fdfed0bef9ec7f17ad7bc9e0065f Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 24 May 2022 21:22:05 +0000 Subject: [PATCH 312/898] Update library/traefik version from v2.6.6 to v2.7.0 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index d6b5775cd9..6e4318e5b3 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -246,7 +246,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.6.6" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.7.0" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 3bb601c70c2d51374dd6cb98af6dfbc24898cb02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 May 2022 05:05:45 +0000 Subject: [PATCH 313/898] build(deps): bump mwoauth from 0.3.7 to 0.3.8 in /images/hub Bumps [mwoauth](https://github.com/mediawiki-utilities/python-mwoauth) from 0.3.7 to 0.3.8. - [Release notes](https://github.com/mediawiki-utilities/python-mwoauth/releases) - [Commits](https://github.com/mediawiki-utilities/python-mwoauth/commits) --- updated-dependencies: - dependency-name: mwoauth dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 84ea7a5da5..01b3a6da6e 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -113,7 +113,7 @@ multidict==6.0.2 # via # aiohttp # yarl -mwoauth==0.3.7 +mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in From b1ec6a27961dea8e53c7f123a40dd0e8443e0123 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 26 May 2022 05:16:30 +0000 Subject: [PATCH 314/898] Update kube-scheduler version from v1.23.6 to v1.23.7 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 6e4318e5b3..8d6cc31aad 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -503,7 +503,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.6" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.7" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 8692f1f4b0f54877a36f07101be05c4c020d46f6 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 27 May 2022 14:59:55 +0530 Subject: [PATCH 315/898] Bump versions of image-awaiter dependencies - Move to a newer go version - Move to a newer version of the HTTP client we use --- images/image-awaiter/Dockerfile | 2 +- images/image-awaiter/go.mod | 6 ++++-- images/image-awaiter/go.sum | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/images/image-awaiter/Dockerfile b/images/image-awaiter/Dockerfile index 5740fc05cb..2fca06a800 100644 --- a/images/image-awaiter/Dockerfile +++ b/images/image-awaiter/Dockerfile @@ -1,5 +1,5 @@ # compile the code to an executable using an intermediary image -FROM golang:1.17 +FROM golang:1.18 # VULN_SCAN_TIME= diff --git a/images/image-awaiter/go.mod b/images/image-awaiter/go.mod index 84a65f3978..bbc772f13c 100644 --- a/images/image-awaiter/go.mod +++ b/images/image-awaiter/go.mod @@ -1,5 +1,7 @@ module github.com/jupyterhub/zero-to-jupyterhub-k8s/image-awaiter -go 1.15 +go 1.18 -require github.com/hashicorp/go-retryablehttp v0.6.7 +require github.com/hashicorp/go-retryablehttp v0.7.1 + +require github.com/hashicorp/go-cleanhttp v0.5.1 // indirect diff --git a/images/image-awaiter/go.sum b/images/image-awaiter/go.sum index 00f0cc1641..270b259c4a 100644 --- a/images/image-awaiter/go.sum +++ b/images/image-awaiter/go.sum @@ -1,8 +1,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo= -github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= +github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= From 62c26b473dd363334034c6c5f2d4fc06a7e2931c Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 27 May 2022 13:03:34 +0100 Subject: [PATCH 316/898] `og:image`: use first image or logo --- docs/source/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index aad2193211..ebc7996820 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -173,6 +173,10 @@ def _get_git_ref_from_chartpress_based_version(version): "advanced": "administrator/advanced", } +# opengraph configuration +# ogp_site_url/prefix is set automatically by RTD +ogp_image = "_static/logo.png" +ogp_use_first_image = True # -- Generate the Helm chart configuration reference from a schema file ------ From 53b2382a2d65153c8120772f878ede8fb6b57128 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 28 May 2022 16:00:58 +0200 Subject: [PATCH 317/898] docs: fix dependabot link --- .github/dependabot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 5d6f51621f..80dc9c4b60 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,4 +1,4 @@ -# dependabot.yml reference: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates +# dependabot.yml reference: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file # # Notes: # - Status and logs from dependabot are provided at From d2861f4719af66aecef9156a417bc971738ca57a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 28 May 2022 16:01:33 +0200 Subject: [PATCH 318/898] fix: k8s 1.20 doesn't have PDBs in v1 yet --- jupyterhub/templates/hub/pdb.yaml | 2 +- jupyterhub/templates/proxy/autohttps/pdb.yaml | 2 +- jupyterhub/templates/proxy/pdb.yaml | 2 +- jupyterhub/templates/scheduling/user-placeholder/pdb.yaml | 2 +- jupyterhub/templates/scheduling/user-scheduler/pdb.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jupyterhub/templates/hub/pdb.yaml b/jupyterhub/templates/hub/pdb.yaml index 5a73211375..3a22e39bdb 100644 --- a/jupyterhub/templates/hub/pdb.yaml +++ b/jupyterhub/templates/hub/pdb.yaml @@ -1,5 +1,5 @@ {{- if .Values.hub.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} {{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} diff --git a/jupyterhub/templates/proxy/autohttps/pdb.yaml b/jupyterhub/templates/proxy/autohttps/pdb.yaml index c67a85ac7f..074d0ac51b 100644 --- a/jupyterhub/templates/proxy/autohttps/pdb.yaml +++ b/jupyterhub/templates/proxy/autohttps/pdb.yaml @@ -1,5 +1,5 @@ {{- if .Values.proxy.traefik.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} {{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} diff --git a/jupyterhub/templates/proxy/pdb.yaml b/jupyterhub/templates/proxy/pdb.yaml index 3280ad0fe7..d8651f56ac 100644 --- a/jupyterhub/templates/proxy/pdb.yaml +++ b/jupyterhub/templates/proxy/pdb.yaml @@ -1,5 +1,5 @@ {{- if .Values.proxy.chp.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} {{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml index 43499a7eec..ec84fb58ce 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml @@ -3,7 +3,7 @@ The cluster autoscaler should be allowed to evict and reschedule these pods if it would help in order to scale down a node. */}} {{- if .Values.scheduling.userPlaceholder.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} {{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml index 47f940c09c..3a9544ef2e 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml @@ -1,5 +1,5 @@ {{- if and .Values.scheduling.userScheduler.enabled .Values.scheduling.userScheduler.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1" -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} {{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 {{- else }} From b4ec4ddee87ed0acf94059eda2b1811e4bddb145 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 00:34:45 +0200 Subject: [PATCH 319/898] ci: reduce frequency of gha/vuln bumps --- .github/dependabot.yaml | 7 +------ .github/workflows/vuln-scan.yaml | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 5d6f51621f..da51226338 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -6,8 +6,6 @@ # - YAML anchors are not supported here or in GitHub Workflows. # - versioning-strategy: lockfile-only must not be used if you only have a plain # requirements.txt like we do in images/singleuser-sample. -# - We explicitly set the "maintenance" label to help our changelog generator -# tool github-activity to categorize PRs. # version: 2 updates: @@ -20,7 +18,6 @@ updates: timezone: "Etc/UTC" versioning-strategy: lockfile-only labels: - - maintenance - dependencies # Maintain Python dependencies for the jupyterhub/k8s-singleuser-sample image @@ -37,16 +34,14 @@ updates: ignore: - dependency-name: jupyterhub labels: - - maintenance - dependencies # Maintain dependencies in our GitHub Workflows - package-ecosystem: github-actions directory: "/" # This should be / rather than .github/workflows schedule: - interval: daily + interval: weekly time: "05:00" timezone: "Etc/UTC" labels: - - maintenance - dependencies diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 656deebf44..332011d1a3 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -13,8 +13,8 @@ on: paths: - ".github/workflows/vuln-scan.yaml" schedule: - # At 00:00 - https://crontab.guru - - cron: "0 0 * * *" + # At 05:00 on Monday - https://crontab.guru + - cron: "0 5 * * 1" workflow_dispatch: jobs: From 0a99fde27a46bfdb77af63b01a56b7ca1a2886d8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 10:51:03 +0200 Subject: [PATCH 320/898] maint: update import statement for py310 compatibility --- jupyterhub/files/hub/z2jh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/files/hub/z2jh.py b/jupyterhub/files/hub/z2jh.py index 85b5e465f3..acc1fb22c4 100644 --- a/jupyterhub/files/hub/z2jh.py +++ b/jupyterhub/files/hub/z2jh.py @@ -3,7 +3,7 @@ Methods here can be imported by extraConfig in values.yaml """ -from collections import Mapping +from collections.abc import Mapping from functools import lru_cache import os From ad18a95659ab0ef07a05322a8238af6019ebb997 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 13:56:46 +0200 Subject: [PATCH 321/898] hub/singleuser-sample image: remove no longer needed --build-arg=PIP_OVERRIDES --- chartpress.yaml | 8 -------- images/hub/Dockerfile | 7 ------- images/singleuser-sample/Dockerfile | 7 ------- 3 files changed, 22 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 863beb9d17..5594084cba 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -22,10 +22,6 @@ charts: # Authenticator are running. hub: valuesPath: hub.image - # Dev: buildArgs can help you try the Helm chart against unreleased - # versions of JupyterHub or KubeSpawner. - # buildArgs: - # PIP_OVERRIDES: "jupyterhub==1.3.0 git+https://github.com/your-username-here/kubespawner.git" # secret-sync, a sidecar container running in the autohttps pod to next to # Traefik meant to sync a TLS certificate with a k8s Secret. @@ -48,7 +44,3 @@ charts: # singleuser-sample, a primitive user container to start with. singleuser-sample: valuesPath: singleuser.image - # Dev: buildArgs can help you try the Helm chart against unreleased - # versions of JupyterHub. - # buildArgs: - # PIP_OVERRIDES: "git+https://github.com/your-username-here/jupyterhub.git" diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 4177c7d351..742242e0e1 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -50,13 +50,6 @@ RUN PYCURL_SSL_LIBRARY=openssl \ pip install --no-cache-dir \ -r /tmp/requirements.txt -# Support overriding a package or two through passed docker --build-args. -# ARG PIP_OVERRIDES="jupyterhub==1.3.0 git+https://github.com/consideratio/kubespawner.git" -ARG PIP_OVERRIDES= -RUN if test -n "$PIP_OVERRIDES"; then \ - pip install --no-cache-dir $PIP_OVERRIDES; \ - fi - WORKDIR /srv/jupyterhub # So we can actually write a db file here diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 0dcf17c322..2afdb607dc 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -23,13 +23,6 @@ COPY requirements.txt /tmp/requirements.txt RUN python -m pip install --no-cache-dir \ -r /tmp/requirements.txt -# Support overriding a package or two through passed docker --build-args. -# ARG PIP_OVERRIDES="jupyterhub==1.3.0" -ARG PIP_OVERRIDES= -RUN if [[ -n "$PIP_OVERRIDES" ]]; then \ - pip install --no-cache-dir $PIP_OVERRIDES; \ - fi - RUN jupyter serverextension enable --py nbgitpuller --sys-prefix # Uncomment the line below to make nbgitpuller default to start up in JupyterLab From 93cef3e599ad9d7989dcab4f7ab5a40b8bb04742 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 14:01:07 +0200 Subject: [PATCH 322/898] singleuser-sample image: adjust notes and formatting --- images/singleuser-sample/Dockerfile | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 2afdb607dc..f61f90b6d5 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -5,10 +5,6 @@ FROM jupyter/base-notebook:latest # VULN_SCAN_TIME=2022-05-15_02:03:52 -# The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. - -# Example install of git and nbgitpuller. -# NOTE: git is already available in the jupyter/minimal-notebook image. USER root RUN apt-get update \ && apt-get upgrade -y \ @@ -20,12 +16,11 @@ RUN apt-get update \ USER $NB_USER COPY requirements.txt /tmp/requirements.txt -RUN python -m pip install --no-cache-dir \ - -r /tmp/requirements.txt +RUN pip install --no-cache-dir \ + -r /tmp/requirements.txt +# nbgitpuller is installed in requirements.txt for demo purposes, and this +# enables it to function. RUN jupyter serverextension enable --py nbgitpuller --sys-prefix -# Uncomment the line below to make nbgitpuller default to start up in JupyterLab -#ENV NBGITPULLER_APP=lab - # conda/pip/apt install additional packages here, if desired. From f27afe858c7b215d13b89dd73db6eb0700bc4492 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 13:49:32 +0200 Subject: [PATCH 323/898] hub image: remove dependencies script, provide alternative command --- .github/workflows/watch-dependencies.yaml | 16 +- RELEASE.md | 3 +- images/hub/Dockerfile | 6 - images/hub/README.md | 27 +++ images/hub/dependencies | 197 ---------------------- images/hub/requirements.in | 5 +- 6 files changed, 43 insertions(+), 211 deletions(-) create mode 100644 images/hub/README.md delete mode 100755 images/hub/dependencies diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index bd649eb543..1fee575247 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -148,12 +148,20 @@ jobs: sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml - - name: Install dependencies for images/hub/dependencies script - run: pip install click ruamel.yaml - - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in if: steps.local.outputs.version != steps.latest.outputs.version - run: images/hub/dependencies freeze --upgrade + # NOTE: If you make an update to this script, also update the + # images/hub/README.md file. + # + run: | + cd images/hub + docker run --rm \ + --env=CUSTOM_COMPILE_COMMAND="see README.md" \ + --volume=$PWD:/io \ + --workdir=/io \ + --user=root \ + python:3.9-bullseye \ + sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' - name: git diff if: steps.local.outputs.version != steps.latest.outputs.version diff --git a/RELEASE.md b/RELEASE.md index c321f02e12..ac9a1d8147 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -14,7 +14,8 @@ The JupyterHub Helm chart relies on many dependent projects, and when we make a ### Dependent Python packages -Update JupyterHub's Python dependencies in `images/hub/requirements.txt` by going to the folder and running `./dependencies freeze --upgrade`. +Update JupyterHub's Python dependencies in `images/hub/requirements.txt` by +following the instructions in `images/hub/README.md`. Also consider nudging dependent projects in the JupyterHub GitHub organization for a release. diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 742242e0e1..2ae51a3648 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -58,11 +58,5 @@ RUN chown ${NB_USER}:${NB_USER} /srv/jupyterhub # JupyterHub API port EXPOSE 8081 -# when building the dependencies image -# add pip-tools necessary for computing dependencies -# this is not done in production builds by chartpress -ARG PIP_TOOLS= -RUN test -z "$PIP_TOOLS" || pip install --no-cache pip-tools==$PIP_TOOLS - USER ${NB_USER} CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] diff --git a/images/hub/README.md b/images/hub/README.md new file mode 100644 index 0000000000..188f1fece0 --- /dev/null +++ b/images/hub/README.md @@ -0,0 +1,27 @@ +# About this folder + +The Dockerfile in this folder is built by +[chartpress](https://github.com/jupyterhub/chartpress#readme), using the +requirements.txt file. The requirements.txt file is updated based on the +requirements.in file using [`pip-compile`](https://pip-tools.readthedocs.io). + +## How to update requirements.txt + +Because `pip-compile` resolves `requirements.txt` with the current Python for +the current platform, it should be run on the same Python version and platform +as our Dockerfile. + +Note that as of 2022-05-29, `pip-compile` has issues with `pycurl`, but we +workaround them by by omitting the `-slim` part from the image in the command +below. + +```shell +# update requirements.txt based on requirements.in +docker run --rm \ + --env=CUSTOM_COMPILE_COMMAND="see README.md" \ + --volume=$PWD:/io \ + --workdir=/io \ + --user=root \ + python:3.9-bullseye \ + sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' +``` diff --git a/images/hub/dependencies b/images/hub/dependencies deleted file mode 100755 index d3166ec12e..0000000000 --- a/images/hub/dependencies +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -"""automatically manage requirements.txt dependencies with pip-tools - -See - - ./dependencies --help for commands and arguments - -How it works: - -- the image used in the helm chart installs a frozen environment from requirements.txt -- `pip-compile` is used to generate frozen `requirements.txt` from our actual requirements - in requirements.in. -- `pip list --outdated` is used to report available updates for packages - in the frozen environment -- pip-compile etc. are run *inside the image* to ensure consistent behavior, - rather than running on host systems, which can vary. -- When building the image to be used for running dependency-management commands, - chartpress configuration is loaded to ensure the environment is the same - as when chartpress builds the tagged image to be published. -""" - -from functools import lru_cache -import json -import os -from subprocess import check_call, check_output - -import click -from ruamel.yaml import YAML - -yaml = YAML() -here = os.path.dirname(os.path.abspath(__file__)) -chartpress_yaml = os.path.join(here, os.pardir, os.pardir, "chartpress.yaml") -values_yaml = os.path.join(here, os.pardir, os.pardir, "jupyterhub", "values.yaml") -dependencies_image = "hub-dependencies" -pip_tools_version = "6.*" - - -@lru_cache() -def build_args(image_name="hub"): - """retrieve docker build arguments from chartpress.yaml config file - - Args: - - image_name (str): - the name of the image to be built in chartpress.yaml - """ - with open(chartpress_yaml) as f: - chartpress_config = yaml.load(f) - chart = chartpress_config["charts"][0] - image_config = chart["images"][image_name] - return image_config.get("buildArgs", {}) - - -def build_image(): - """Build the docker image used for computing dependencies - - This runs the chartpress build of the current image - with the addition of pip-tools, used for computing dependencies. - - The image is built with the current frozen environment in requirements.txt - and pip-tools commands are available for updating requirements.txt from requirements.in. - """ - click.echo(f"Building docker image {dependencies_image}") - build_arg_dict = build_args() - build_arg_list = ["--build-arg", f"PIP_TOOLS={pip_tools_version}"] - for key in sorted(build_arg_dict): - value = build_arg_dict[key] - build_arg_list.append("--build-arg") - build_arg_list.append(f"{key}={value}") - check_call(["docker", "build", "-t", dependencies_image] + build_arg_list + [here]) - - -@click.group() -def cli(): - """Manage the Python dependencies in this image.""" - pass - - -@click.command() -@click.option( - "--build/--no-build", - help="add --no-build to skip building the dependencies image prior to upgrading", - default=True, -) -@click.option( - "--upgrade/--no-upgrade", - help="--upgrade to upgrade all dependencies within the range specified in requirements.in", - default=False, -) -@click.option( - "--upgrade-package", - help="specify individual packages to upgrade within the range specified in requirements.in", - multiple=True, -) -def freeze(build, upgrade, upgrade_package): - """Freeze the environment, updating requirements.txt from requirements.in - - Individual packages can be updated, or the whole environment. - - This command: - - 1. builds the image with the current frozen environment - 2. runs pip-compile in the image to update requirements.txt from requirements.in, - passing through additional arguments to pip-compile - """ - if build: - build_image() - click.echo("freezing dependencies with pip-compile") - upgrade_args = [] - if upgrade: - upgrade_args.append("--upgrade") - for pkg in upgrade_package: - upgrade_args.append("--upgrade-package") - upgrade_args.append(pkg) - check_call( - [ - "docker", - "run", - "--rm", - "--env=CUSTOM_COMPILE_COMMAND=./dependencies freeze --upgrade", - "--user=root", - f"--volume={here}:/io", - "--workdir=/io", - dependencies_image, - "pip-compile", - ] - + upgrade_args - ) - - -cli.add_command(freeze) - - -@click.command() -@click.option("--build/--no-build", default=True) -def outdated(build): - """Check for outdated dependencies with pip. - - This command: - - 1. builds the image with the current frozen environment - 2. runs `pip list --outdated` to report any outdated packages - that could be candidates for upgrade - """ - if build: - build_image() - click.echo("Checking for outdated dependencies with pip.") - outdated_json = check_output( - [ - "docker", - "run", - "--rm", - dependencies_image, - "pip", - "list", - "--outdated", - "--format=json", - ] - ).decode("utf8") - outdated = json.loads(outdated_json) - have_outdated = False - for pkg in outdated: - name = pkg["name"] - # ignore some common packages that aren't relevant to our requirements.txt - if name in {"pip", "setuptools", "wheel"}: - continue - have_outdated = True - version = pkg["version"] - latest = pkg["latest_version"] - # TODO: parser requirements.in to check if latest is in-range? - # If they are in-range, running freeze again is enough, - # but if they are outside the range, requirements.in needs to be updated - # first to pick them up - # for now, print as much so humans can decide - print(f"Have {name}=={version}, latest is {name}=={latest}") - - if have_outdated: - print("There are outdated dependencies!") - print( - "To pick up any versions outside the range(s) specified in requirements.in," - ) - print("update the pinning(s) in that file.") - print( - "To update the whole environment within the given ranges, run `./dependencies freeze --upgrade`" - ) - print( - "To update one or more specific packages, run `./dependencies freeze --upgrade-package pkg1 [--upgrade-package pkg2]`" - ) - else: - print("Everything appears to be up-to-date!") - - -cli.add_command(outdated) - - -if __name__ == "__main__": - cli() diff --git a/images/hub/requirements.in b/images/hub/requirements.in index d253d8f7f5..b27d3ccf96 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -1,7 +1,6 @@ # This file is automatically maintained by Dependabot, as configured in -# .github/dependabot.yaml. It can also be updated by... -# -# ./dependencies freeze --upgrade +# .github/dependabot.yaml. It can also be updated by a command described in the +# README.md file. # # JupyterHub itself From f4c7e89ebbc357e7982208ef3e920b86f0733dae Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 14:06:04 +0200 Subject: [PATCH 324/898] hub image: use python:3.9-slim-bullseye as base, install pycurl with libcurl4, etc --- images/hub/Dockerfile | 95 +++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 2ae51a3648..9162f8deba 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,62 +1,71 @@ -FROM ubuntu:20.04 +# The build stage +# --------------- +FROM python:3.9-bullseye as build-stage # VULN_SCAN_TIME=2022-05-15_02:03:57 -ENV DEBIAN_FRONTEND=noninteractive \ - LANG=C.UTF-8 +WORKDIR /build-stage -# psycopg2-binary in requirements.txt is not compiled for linux/arm64 -# TODO: Use build stages to compile psycopg2-binary separately instead of -# bloating the image size -RUN EXTRA_APT_PACKAGES=; \ - if [ `uname -m` != 'x86_64' ]; then EXTRA_APT_PACKAGES=libpq-dev; fi; \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get install -y --no-install-recommends \ - git \ - vim \ - less \ - python3 \ - python3-dev \ - python3-pip \ - python3-setuptools \ - python3-wheel \ - libssl-dev \ - libcurl4-openssl-dev \ - build-essential \ - sqlite3 \ - curl \ - dnsutils \ - $EXTRA_APT_PACKAGES \ - && \ - rm -rf /var/lib/apt/lists/* +# Build wheels for packages that requires gcc and other build dependencies and +# lack wheels either for amd64 or aarch64. If you find a dependency here no +# longer listed in requirements.txt, remove it from here as well. +# +COPY requirements.txt requirements.txt +RUN pip install build \ + && pip wheel \ + $(cat requirements.txt | grep "pycryptodomex==") \ + $(cat requirements.txt | grep "pycurl==") \ + $(cat requirements.txt | grep "ruamel-yaml-clib==") + + +# The final stage +# --------------- +FROM python:3.9-slim-bullseye ARG NB_USER=jovyan ARG NB_UID=1000 ARG HOME=/home/jovyan +ENV DEBIAN_FRONTEND=noninteractive \ + LANG=C.UTF-8 RUN adduser --disabled-password \ - --gecos "Default user" \ - --uid ${NB_UID} \ - --home ${HOME} \ - --force-badname \ - ${NB_USER} + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y --no-install-recommends \ + # misc network utilities + curl \ + dnsutils \ + git \ + # misc other utilities + less \ + vim \ + # requirement for pycurl + libcurl4 \ + # requirement for using a local sqlite database + sqlite3 \ + && rm -rf /var/lib/apt/lists/* + +RUN if [ "$(uname -m)" = x86_64 ]; then ARCH=amd64; fi; \ + if [ "$(uname -m)" = aarch64 ]; then ARCH=arm64; fi; \ + curl -sSLo /tini "https://github.com/krallin/tini/releases/download/v0.19.0/tini-$ARCH" \ + && chmod +x /tini + +COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ COPY requirements.txt /tmp/requirements.txt -RUN pip3 install --upgrade --no-cache-dir \ - setuptools \ - pip -RUN PYCURL_SSL_LIBRARY=openssl \ - pip install --no-cache-dir \ +RUN pip install --no-cache-dir \ + /tmp/pre-built-wheels/*.whl \ -r /tmp/requirements.txt WORKDIR /srv/jupyterhub - -# So we can actually write a db file here RUN chown ${NB_USER}:${NB_USER} /srv/jupyterhub +USER ${NB_USER} -# JupyterHub API port EXPOSE 8081 - -USER ${NB_USER} +ENTRYPOINT ["/tini", "--"] CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] From fa81c67f1e905a6de75483b680584e69a77c6a52 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 14:06:13 +0200 Subject: [PATCH 325/898] hub image: update requirements.txt --- images/hub/requirements.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 01b3a6da6e..f4dce5a48e 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile +# This file is autogenerated by pip-compile with python 3.9 # To update, run: # -# ./dependencies freeze --upgrade +# see README.md # aiohttp==3.8.1 # via kubernetes-asyncio @@ -22,7 +22,7 @@ bcrypt==3.2.2 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2021.10.8 +certifi==2022.5.18.1 # via # kubernetes-asyncio # requests @@ -97,9 +97,9 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jwcrypto==1.2 +jwcrypto==1.3.1 # via jupyterhub-ltiauthenticator -kubernetes-asyncio==22.6.4 +kubernetes-asyncio==22.6.5 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -164,7 +164,7 @@ pyopenssl==22.0.0 # via # certipy # josepy -pyparsing==3.0.8 +pyparsing==3.0.9 # via packaging pyrsistent==0.18.1 # via jsonschema @@ -216,7 +216,7 @@ tornado==6.1 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.1.1 +traitlets==5.2.1.post0 # via # jupyter-telemetry # jupyterhub From d52c245972f19fc2d834ab731f871d9caa922120 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 30 May 2022 05:27:28 +0000 Subject: [PATCH 326/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 4177c7d351..38b8d1a440 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:20.04 -# VULN_SCAN_TIME=2022-05-15_02:03:57 +# VULN_SCAN_TIME=2022-05-30_05:27:26 ENV DEBIAN_FRONTEND=noninteractive \ LANG=C.UTF-8 From e357471cf31c00cdd62e688952c7115cfa96d9db Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 30 May 2022 05:27:32 +0000 Subject: [PATCH 327/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 0dcf17c322..673475c607 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-05-15_02:03:52 +# VULN_SCAN_TIME=2022-05-30_05:27:31 # The jupyter/docker-stacks images contains jupyterhub and jupyterlab already. From bf3bad68d6609265d474d675e56e935d43f4110a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 30 May 2022 05:27:32 +0000 Subject: [PATCH 328/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index f61f90b6d5..5f58ed3617 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-05-15_02:03:52 +# VULN_SCAN_TIME=2022-05-30_05:27:31 USER root RUN apt-get update \ From 7a86789b0b8d53e2ac35ef01315f9f171583f8c4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 01:27:57 +0200 Subject: [PATCH 329/898] docs: update documentation outside configuration reference --- docs/source/administrator/security.md | 235 +++++++++++++++----------- jupyterhub/schema.yaml | 4 + 2 files changed, 141 insertions(+), 98 deletions(-) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 91d70cf342..60460ed4b7 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -285,118 +285,157 @@ singleuser: ## Kubernetes Network Policies -**Important**: When using network policies, you should be aware -that a Kubernetes cluster may have partial, full, or no support for network policies. -Kubernetes will **silently ignore** policies that aren't supported. -Please use **caution** before relying on network policy enforcement -and verify the policies behave as expected, -especially if you rely on them to restrict what users can access. - -Kubernetes has optional support for [network -policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) -which lets you restrict how pods can communicate with each other and the outside -world. This can provide additional security within JupyterHub, and can also be -used to limit network access for users of JupyterHub. - -By default, the JupyterHub helm chart **enables** network policies in 0.10 or later. -They are **disabled** by default in 0.9 and earlier. - -The JupyterHub chart has three network policies, -one for each component (hub, proxy, single-user servers), -which can be enabled and configured separately. +```{warning} +Your Kubernetes cluster may silently ignore the network rules described in the +NetworkPolicy resources that this Helm chart can create. NetworkPolicy rules are +enforced by an optional NetworkPolicy controller that often isn't setup as part +of setting up a Kubernetes cluster. +``` -### Enabling and disabling network policies +By default this Helm chart creates four different +[NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) +resources describing what incoming/ingress and outgoing/egress connections are +to be allowed for the pods they target. + +A critical point to understand is that if a pod's ingress or egress connections +respectively aren't targeted by a NetworkPolicy, they won't be constrained by +them at all. If they are though, only what is explicitly allowed for them will +be accepted. In other words, the act of defining a NetworkPolicy targeting a pod +is what is constraining it, but all the rules in the NetworkPolicy are allow +rules. + +### Introduction to the chart's four network policies + +The four network policies declare rules for four kinds of pods created by the +Helm chart. Below are some tables describing what the four network policy do to +some extent. + +| NetworkPolicy | Associated Helm chart config | Influenced pods | Notable software in pods | +| ------------- | ----------------------------- | -------------------- | -------------------------------------------------------------------- | +| `hub` | `hub.networkPolicy` | `hub` | `jupyterhub`, `kubespawner`, `jupyterhub-idle-culler`, Authenticator | +| `proxy` | `proxy.chp.networkPolicy` | `proxy` | `configurable-http-proxy` | +| `autohttps` | `proxy.traefik.networkPolicy` | `autohttps` | `traefik`, `lego` | +| `singleuser` | `singleuser.networkPolicy` | `jupyter-` | `jupyter_server` | + +| NetworkPolicy | Always allowed outbound connections (egress) for core functionality | +| ------------- | ---------------------------------------------------------------------------------------------------------------- | +| `hub` | To `proxy` pod's REST API port (8001), user pods' only port (8888) | +| `proxy` | To `hub` pod's only port (8081), user pods' only port (8888) | +| `autohttps` | To `proxy` pod's http proxy port (8000) | +| `singleuser` | To `hub` pod's only port (8081), `proxy` pod's proxy port (8000), `autohttps` pod's http (8080) and https (8443) | + +| NetworkPolicy | Always allowed inbound connections (ingress) for core functionality, ingress is allowed for specific ports from pods with certain labels | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `hub` | From pods labelled `hub.jupyter.org/network-access-hub=true` | +| `proxy` | From pods labelled `hub.jupyter.org/network-access-proxy-http=true` (http proxy port) or `hub.jupyter.org/network-access-proxy-api=true` (REST API port) in the same namespace | +| `autohttps` | From pods labelled `hub.jupyter.org/network-access-proxy-http=true` (http(s) proxy ports) | +| `singleuser` | From pods labelled `hub.jupyter.org/network-access-singleuser=true` (notebook-port) | + +````{warning} Not all functionality summarized above +It has been tricky to document the full behavior of these network policies. For +in depth details, please for now refer to inspecting the Helm chart's templates +and the rendered result given your configuration. + +Below are links to the Helm chart's templates. + +- [`hub` template](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/templates/hub/netpol.yaml) +- [`proxy` template](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/templates/proxy/netpol.yaml) +- [`autohttps` template](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/templates/proxy/autohttps/netpol.yaml) +- [`singleuser` template](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/templates/singleuser/netpol.yaml) + +Below are commands you can use to render the specific template. + +```shell +# These four commands renders the four NetworkPolicy resource templates of the +# latest release of the JupyterHub Helm chart, with default values. +# +# You can pass `--values ` or `--version ` to +# these commands to inspect the rendered NetworkPolicy resources given your +# specific version and configuration. +# +helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/hub/netpol.yaml +helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/proxy/netpol.yaml +helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/proxy/autohttps/netpol.yaml +helm template --repo https://jupyterhub.github.io/helm-chart jupyterhub --show-only templates/singleuser/netpol.yaml +``` +```` -By default, the JupyterHub helm chart **enables** network policies in 0.10 or later. -They are **disabled** by default in 0.9 and earlier. +### Enabling and disabling network policies -You can enable or disable enforcement of each network policy in config.yaml: +NetworkPolicy resources are created by default, and with their creation they +restrict inbound and outbound network connections to those explicitly allowed in +the NetworkPolicy resource. To opt-out of creating NetworkPolicy resources, use +configuration like below. ```yaml +# Example configuration on how to disable the creation of all the Helm chart's +# NetworkPolicy resources. hub: networkPolicy: - enabled: true # or false to disable + enabled: false proxy: - networkPolicy: - enabled: true + chp: + networkPolicy: + enabled: false + traefik: + networkPolicy: + enabled: false singleuser: networkPolicy: - enabled: true + enabled: false ``` -### Granting network access to jupyterhub pods (ingress) - -The chart's network policy default behavior ensures that all of the jupyterhub components can talk to each other, -so all of the following connections are allowed: - -- proxy ⇨ hub -- proxy ⇨ singleuser -- hub ⇨ proxy api -- hub ⬄ singleuser -- everything ⇨ DNS - -and by default do not allow any other pods to talk to the jupyterhub components. - -The network policies use label selectors that look like: - -```yaml -ingress: - # allowed pods (hub.jupyter.org/network-access-hub) --> hub - - from: - - podSelector: - matchLabels: - hub.jupyter.org/network-access-hub: "true" +### Allowing additional inbound network connections (ingress) + +While you can add allow arbitrary allow rules with the +[`.networkPolicy.ingress`](schema_hub.networkPolicy.ingress) +configuration besides the rules ensuring core functionality, you can also label +the pods you want to be allowed to establish connections to the Helm chart's +various pods. + +For example, to access the hub pod from another pod in the same namespace, just +add the label `hub.jupyter.org/network-access-hub: "true"` to the pod that +should be able to establish a connection to the hub pod. + +The available access labels are: + +- `hub.jupyter.org/network-access-hub: "true"`, access the hub api +- `hub.jupyter.org/network-access-proxy-http: "true"`, access proxy public http endpoint +- `hub.jupyter.org/network-access-proxy-api: "true"`, access proxy api +- `hub.jupyter.org/network-access-singleuser: "true"`, access singleuser servers directly + +If you wish to access the pod from another namespace with these labels, then +read about +[`.networkPolicy.interNamespaceAccessLabels`](schema_hub.networkPolicy.interNamespaceAccessLabels). + +Finally, the option +[`.networkPolicy.allowedIngressPorts`](schema_hub.networkPolicy.allowedIngressPorts) +enable you to allow incoming connections on certain pods. + +### Allowing additional outbound network connections (egress) + +While you can add allow arbitrary allow rules with the +[`.networkPolicy.egress`](schema_hub.networkPolicy.egress) +configuration besides the rules ensuring core functionality, you can also toggle +some pre-defined allow rules on or off. They are documented in the configuration +reference under +[`.networkPolicy.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules). + +By default, all egress allow rules are enabled for `hub`, `proxy.chp`, and +`proxy.traefik`, but +`singleuser.networkPolicy.egressAllowRules.cloudMetadataServer` and +`singleuser.networkPolicy.egressAllowRules.privateIPs` default to false. In +practice, this can mean no rule allows the user pods to communicate with some +k8s local service with [Private IPv4 +addresses](https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses). + +```{versionchanged} 2.0.0 +Before JupyterHub Helm chart 2.0.0 the default configuration was to allow +singleuser pods to establish outbound connections to anything. After 2.0.0 +`singleuser.networkPolicy.egressAllowRules.privateIPs=true` must be explicitly +set for this. ``` -So if you are creating additional pods that want to talk to these, -you can grant them access to jupyterhub components one by one by adding the right labels. -Here is an example set of labels granting access to all jupyterhub components -(i.e. the same behavior as without network policies): - -```yaml -metadata: - name: my-service - labels: - hub.jupyter.org/network-access-hub: "true" # access the hub api - hub.jupyter.org/network-access-proxy-http: "true" # access proxy public http endpoint - hub.jupyter.org/network-access-proxy-api: "true" # access proxy api - hub.jupyter.org/network-access-singleuser: "true" # access single-user servers directly -``` - -You can also add additional `ingress` rules to each network policy in your `config.yaml`. -See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/network-policies/) -for how to define ingress rules. - -### Limiting network access from pods (egress) - -By default, all of the pods allow all `egress` traffic, -which means that code in each of the pods may make connections to anywhere in the cluster or on the Internet -(unless that would be blocked by the ingress rules of the destination). -This is very permissive. -The default policy for all components allows all outbound (egress) network traffic, -meaning JupyterHub users are able to connect to all resources inside and outside your network. -You can override the `egress` configuration of each policy -to make it more restrictive. -For example, to restrict user outbound traffic to DNS, HTTP, and HTTPS: - -```yaml -singleuser: - networkPolicy: - enabled: true - egress: - - ports: - - port: 53 - protocol: UDP - - ports: - - port: 80 - - ports: - - port: 443 -``` - -See the [Kubernetes -documentation](https://kubernetes.io/docs/concepts/services-networking/network-policies/) -for further information on defining policies. - ## Restricting Load Balancer Access By default any IP address can access your JupyterHub deployment through the load balancer service. diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 02accdffa7..0d5884ebc6 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -607,6 +607,10 @@ properties: with the other flags to a reduced set of rules to avoid a performance penalty. ``` + + ```{versionadded} 2.0.0 + All `egressAllowRules` are new in JupyterHub Helm chart 2.0.0. + ``` properties: cloudMetadataServer: type: boolean From e7e2973cb3071ff16b1ca76b9376a461d984c145 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 01:48:38 +0200 Subject: [PATCH 330/898] docs: add note about privateIPs where they occur --- jupyterhub/templates/_helpers-netpol.tpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 3778e51419..5adbd3d275 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -22,6 +22,9 @@ {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} {{- . | nindent 4 }} {{- end }} + + Note that the reference to privateIPs and nonPrivateIPs relate to + https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses. */}} {{- define "jupyterhub.networkPolicy.renderEgressRules" -}} From d3b650bbd91e1a4d660c2776a906bfb9d4aaef9d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 01:52:31 +0200 Subject: [PATCH 331/898] Apply suggestions from code review Co-authored-by: Simon Li --- jupyterhub/schema.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 0d5884ebc6..f59229cefc 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -667,6 +667,12 @@ properties: having this set to false can be a good defence against malicious intent from someone in control of software in these pods. + + To improve security we recommend only allowing the private IP CIDRs required + + or namespaces + + instead of enabling this. interNamespaceAccessLabels: enum: [accept, ignore] description: | From 668b587c14167d6147071a42f16c8acfb683f61a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 02:05:05 +0200 Subject: [PATCH 332/898] docs: suggest not allowing egress to all privateIPs --- jupyterhub/schema.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index f59229cefc..982695d0d5 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -664,15 +664,13 @@ properties: Since not all workloads in the k8s cluster may have NetworkPolicies setup to restrict their incoming connections, - having this set to false can be a good defence against + having this set to false can be a good defense against malicious intent from someone in control of software in these pods. - To improve security we recommend only allowing the private IP CIDRs required - - or namespaces - - instead of enabling this. + If possible, try to avoid setting this to true as it gives + broad permissions that could be specified more directly via + the [`.egress`](schema_singleuser.networkPolicy.egress). interNamespaceAccessLabels: enum: [accept, ignore] description: | From be53687c837c9ec956e77acc1ac22b872bcacab1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 02:48:06 +0200 Subject: [PATCH 333/898] docs: update .egress and .ingress documentation --- jupyterhub/schema.yaml | 39 ++++++++++++++++--- tools/templates/lint-and-validate-values.yaml | 8 +++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 982695d0d5..17cdf70c5d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -575,19 +575,48 @@ properties: enabled: type: boolean description: | - Toggle the creation of the NetworkPolicy resource for this pod. + Toggle the creation of the NetworkPolicy resource targeting this + pod, and by doing so, restricting its communication to only what + is explicitly allowed in the NetworkPolicy. ingress: type: array description: | - Additional ingress rules to add except those that is known to be needed by the respective pods in the Helm chart. + Additional ingress rules to add besides those that are required + for core functionality. egress: type: array description: | - Additional egress rules to add except those that is known to be needed by the respective pods in the Helm chart. + Additional egress rules to add besides those that are required for + core functionality and those added via + [`.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules). - The default value of this egress is to allow all traffic, except for the `singleuser.networkPolicy.egress`, which is also limiting access to a metadata server that can be exploited. + ```{versionchanged} 2.0.0 + The default value changed from providing one very permissive rule + allowing all egress to providing no rule. The permissive rule is + still provided via + [`.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules) + set to true though. + ``` + + As an example, below is a configuration that disables the more + broadly permissive `.privateIPs` egress allow rule for the hub + pod, and instead provides tightly scoped permissions to access a + specific k8s local service as identified by pod labels. - If you want to restrict egress, you can override this permissive default to be an empty list. + ```yaml + hub: + networkPolicy: + egressAllowRules: + privateIPs: false + egress: + - to: + - podSelector: + matchLabels: + app: my-k8s-local-service + ports: + - protocol: TCP + port: 5978 + ``` egressAllowRules: type: object additionalProperties: false diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 7b02fd71f4..36e0868059 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -181,8 +181,12 @@ hub: privateIPs: false egress: - to: - - ipBlock: - cidr: 0.0.0.0/0 + - podSelector: + matchLabels: + app: my-k8s-local-service + ports: + - protocol: TCP + port: 5978 interNamespaceAccessLabels: ignore allowedIngressPorts: [] allowNamedServers: true From 255df3d593cd13bd9bb633242e63aaffae3102f7 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Thu, 2 Jun 2022 10:29:53 +0200 Subject: [PATCH 334/898] feat!: Implement `serviceAccount.create` This feature introduces a new `.serviceAccount.create` for all `ServiceAccount` resources, which defaults to `true`. BREAKING CHANGE: This is a breaking change for all users with `rbac.enabled` set to `false`. --- jupyterhub/schema.yaml | 4 ++++ jupyterhub/templates/hub/deployment.yaml | 2 +- jupyterhub/templates/hub/rbac.yaml | 5 ++++- jupyterhub/templates/image-puller/job.yaml | 2 +- jupyterhub/templates/image-puller/rbac.yaml | 5 ++++- .../templates/proxy/autohttps/deployment.yaml | 2 +- .../templates/proxy/autohttps/rbac.yaml | 21 ++++++++++++------- .../scheduling/user-scheduler/deployment.yaml | 2 +- .../scheduling/user-scheduler/rbac.yaml | 4 +++- jupyterhub/values.yaml | 4 ++++ 10 files changed, 36 insertions(+), 15 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index c13067412e..1e2829cb24 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1259,6 +1259,10 @@ properties: Configuration for a k8s ServiceAccount dedicated for use by the specific pod which this configuration is nested under. properties: + create: + type: boolean + description: | + Whether or not to create the `ServiceAccount` resource annotations: type: object additionalProperties: false diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index 321cd88640..c58d9c64f0 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -75,7 +75,7 @@ spec: persistentVolumeClaim: claimName: {{ include "jupyterhub.hub-pvc.fullname" . }} {{- end }} - {{- if .Values.rbac.enabled }} + {{- if .Values.hub.serviceAccount.create }} serviceAccountName: {{ include "jupyterhub.hub.fullname" . }} {{- end }} {{- with .Values.hub.podSecurityContext }} diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml index 81a78fbe8f..e61fd90fa8 100644 --- a/jupyterhub/templates/hub/rbac.yaml +++ b/jupyterhub/templates/hub/rbac.yaml @@ -1,4 +1,5 @@ -{{- if .Values.rbac.enabled -}} +{{- if .Values.hub.serviceAccount.create -}} +--- apiVersion: v1 kind: ServiceAccount metadata: @@ -9,6 +10,8 @@ metadata: {{- end }} labels: {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} +{{- if .Values.rbac.enabled -}} --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml index dff648c6e8..d960adc9a7 100644 --- a/jupyterhub/templates/image-puller/job.yaml +++ b/jupyterhub/templates/image-puller/job.yaml @@ -34,7 +34,7 @@ spec: {{- end }} spec: restartPolicy: Never - {{- if .Values.rbac.enabled }} + {{- if .Values.prePuller.hook.serviceAccount.create }} serviceAccountName: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} {{- end }} {{- with .Values.prePuller.hook.nodeSelector }} diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index 572524a836..78fd68e7a6 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -1,11 +1,12 @@ {{- /* Permissions to be used by the hook-image-awaiter job */}} -{{- if .Values.rbac.enabled }} +{{- if .Values.prePuller.hook.serviceAccount.create }} {{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} {{- /* This service account... */ -}} +--- apiVersion: v1 kind: ServiceAccount metadata: @@ -20,6 +21,8 @@ metadata: {{- with .Values.prePuller.hook.serviceAccount.annotations }} {{- . | toYaml | nindent 4 }} {{- end }} +{{- end }} +{{- if .Values.rbac.enabled }} --- {{- /* ... will be used by this role... diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 59303f7e69..6efe72af9e 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -27,7 +27,7 @@ spec: # entrypoint of all network traffic. checksum/static-config: {{ include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | sha256sum }} spec: - {{- if .Values.rbac.enabled }} + {{- if .Values.proxy.traefik.serviceAccount.create }} serviceAccountName: {{ include "jupyterhub.autohttps.fullname" . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index 9cdb62c962..e29cf544a5 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -1,6 +1,17 @@ {{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} {{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} -{{- if (and $autoHTTPS .Values.rbac.enabled) -}} +{{- if $autoHTTPS -}} +{{- if .Values.proxy.traefik.serviceAccount.create }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} +{{- if .Values.rbac.enabled }} +--- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: @@ -30,11 +41,5 @@ roleRef: kind: Role name: {{ include "jupyterhub.autohttps.fullname" . }} apiGroup: rbac.authorization.k8s.io ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} {{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 78406a1582..0e82ad6eed 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -17,7 +17,7 @@ spec: annotations: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} spec: - {{- if .Values.rbac.enabled }} + {{- if .Values.scheduling.userScheduler.serviceAccount.create }} serviceAccountName: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index cc6b263a3e..c91f550e6f 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -1,5 +1,5 @@ {{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.rbac.enabled }} +{{- if .Values.scheduling.userScheduler.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: @@ -10,6 +10,8 @@ metadata: annotations: {{- . | toYaml | nindent 4 }} {{- end }} +{{- end }} +{{- if .Values.rbac.enabled }} --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 8d6cc31aad..e87f064e55 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -137,6 +137,7 @@ hub: timeoutSeconds: 1 existingSecret: serviceAccount: + create: true annotations: {} extraPodSpec: {} @@ -278,6 +279,7 @@ proxy: maxUnavailable: minAvailable: 1 serviceAccount: + create: true annotations: {} extraPodSpec: {} secretSync: @@ -514,6 +516,7 @@ scheduling: minAvailable: resources: {} serviceAccount: + create: true annotations: {} extraPodSpec: {} podPriority: @@ -593,6 +596,7 @@ prePuller: tolerations: [] resources: {} serviceAccount: + create: true annotations: {} continuous: enabled: true From c90d7394e99eca175ae303473d8b27b3951d1ebe Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 29 May 2022 10:22:55 +0200 Subject: [PATCH 335/898] maint: assume py38+ in hub image --- .pre-commit-config.yaml | 7 +++++-- jupyterhub/files/hub/z2jh.py | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6656e099ed..cdd2564696 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,14 +25,17 @@ repos: hooks: - id: pyupgrade args: - - --py36-plus + - --py38-plus # Autoformat: Python code - repo: https://github.com/psf/black rev: 22.3.0 hooks: - id: black - args: [--target-version=py36] + args: + - --target-version=py38 + - --target-version=py39 + - --target-version=py310 # Autoformat: Bash scripts - repo: https://github.com/lovesegfault/beautysh diff --git a/jupyterhub/files/hub/z2jh.py b/jupyterhub/files/hub/z2jh.py index acc1fb22c4..0ff662f32f 100644 --- a/jupyterhub/files/hub/z2jh.py +++ b/jupyterhub/files/hub/z2jh.py @@ -10,7 +10,7 @@ import yaml # memoize so we only load config once -@lru_cache() +@lru_cache def _load_config(): """Load the Helm chart configuration used to render the Helm templates of the chart from a mounted k8s Secret, and merge in values from an optionally @@ -29,7 +29,7 @@ def _load_config(): return cfg -@lru_cache() +@lru_cache def _get_config_value(key): """Load value from the k8s ConfigMap given a key.""" @@ -41,7 +41,7 @@ def _get_config_value(key): raise Exception(f"{path} not found!") -@lru_cache() +@lru_cache def get_secret_value(key, default="never-explicitly-set"): """Load value from the user managed k8s Secret or the default k8s Secret given a key.""" From 90b2d2974a00935ab5637c49347c4b12cf3f36ce Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 12:44:36 +0200 Subject: [PATCH 336/898] pre-commit: add isort hook --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cdd2564696..7c21ad70c7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,6 +37,14 @@ repos: - --target-version=py39 - --target-version=py310 + # Autoformat: Python code + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + args: + - --profile=black + # Autoformat: Bash scripts - repo: https://github.com/lovesegfault/beautysh rev: v6.2.1 From 3ee385138771884c752176b33551e22c579fd63f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 12:46:41 +0200 Subject: [PATCH 337/898] pre-commit: run isort hook --- docs/source/conf.py | 1 - jupyterhub/files/hub/jupyterhub_config.py | 7 +++---- jupyterhub/files/hub/z2jh.py | 3 ++- tests/conftest.py | 3 +-- tools/compare-values-schema-content.py | 3 +-- tools/generate-json-schema.py | 1 - tools/set-chart-yaml-annotations.py | 1 - tools/templates/lint-and-validate.py | 4 ++-- tools/validate-against-schema.py | 3 ++- 9 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index ebc7996820..bfe40109cf 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,6 @@ import yaml - # -- Sphinx setup function --------------------------------------------------- # ref: http://www.sphinx-doc.org/en/latest/extdev/tutorial.html#the-setup-function diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index d3f9ffb3c5..5ce84a5b99 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -2,12 +2,11 @@ import os import re import sys - from binascii import a2b_hex -from tornado.httpclient import AsyncHTTPClient -from kubernetes_asyncio import client from jupyterhub.utils import url_path_join +from kubernetes_asyncio import client +from tornado.httpclient import AsyncHTTPClient # Make sure that modules placed in the same directory as the jupyterhub config are added to the pythonpath configuration_directory = os.path.dirname(os.path.realpath(__file__)) @@ -15,10 +14,10 @@ from z2jh import ( get_config, - set_config_if_not_none, get_name, get_name_env, get_secret_value, + set_config_if_not_none, ) diff --git a/jupyterhub/files/hub/z2jh.py b/jupyterhub/files/hub/z2jh.py index 0ff662f32f..3735169b2b 100644 --- a/jupyterhub/files/hub/z2jh.py +++ b/jupyterhub/files/hub/z2jh.py @@ -3,12 +3,13 @@ Methods here can be imported by extraConfig in values.yaml """ +import os from collections.abc import Mapping from functools import lru_cache -import os import yaml + # memoize so we only load config once @lru_cache def _load_config(): diff --git a/tests/conftest.py b/tests/conftest.py index 6f3633f4ea..69dddf7f29 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,13 +4,12 @@ """ import os -import requests import textwrap import uuid - from urllib.parse import urlparse import pytest +import requests import yaml diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py index d66ec505f7..d66f35c04f 100755 --- a/tools/compare-values-schema-content.py +++ b/tools/compare-values-schema-content.py @@ -15,12 +15,11 @@ containerSecurityContext, readiness- and livenessProbe's, and hub.config. """ -import jsonschema import os import sys - from collections.abc import MutableMapping +import jsonschema import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index 0b8bf74c69..8c4396c0cf 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -12,7 +12,6 @@ import json import os import sys - from collections.abc import MutableMapping import yaml diff --git a/tools/set-chart-yaml-annotations.py b/tools/set-chart-yaml-annotations.py index 8b02a968fe..6dfd034803 100755 --- a/tools/set-chart-yaml-annotations.py +++ b/tools/set-chart-yaml-annotations.py @@ -12,7 +12,6 @@ import os import textwrap - from collections.abc import MutableMapping import yaml diff --git a/tools/templates/lint-and-validate.py b/tools/templates/lint-and-validate.py index 07b266bccd..e0941a1de4 100755 --- a/tools/templates/lint-and-validate.py +++ b/tools/templates/lint-and-validate.py @@ -14,12 +14,12 @@ pip install yamllint """ -import os -import sys import argparse import glob +import os import pipes import subprocess +import sys os.chdir(os.path.dirname(sys.argv[0])) diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index d9e9cae10e..c397994a7c 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 -import jsonschema import os import sys + +import jsonschema import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) From f04e571c611ea829805f17ec1ab97a72fa5d92c3 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Thu, 2 Jun 2022 11:41:31 +0200 Subject: [PATCH 338/898] chore: Move `ServiceAccount` resources out of rbac.yaml and into their own template --- jupyterhub/templates/hub/rbac.yaml | 13 ---------- jupyterhub/templates/hub/serviceaccount.yaml | 13 ++++++++++ jupyterhub/templates/image-puller/rbac.yaml | 25 +------------------ .../image-puller/serviceaccount.yaml | 22 ++++++++++++++++ .../templates/proxy/autohttps/rbac.yaml | 9 ------- .../proxy/autohttps/serviceaccount.yaml | 13 ++++++++++ .../scheduling/user-scheduler/rbac.yaml | 12 --------- .../user-scheduler/serviceaccount.yaml | 14 +++++++++++ 8 files changed, 63 insertions(+), 58 deletions(-) create mode 100644 jupyterhub/templates/hub/serviceaccount.yaml create mode 100644 jupyterhub/templates/image-puller/serviceaccount.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/serviceaccount.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml index e61fd90fa8..1d819e03d2 100644 --- a/jupyterhub/templates/hub/rbac.yaml +++ b/jupyterhub/templates/hub/rbac.yaml @@ -1,16 +1,3 @@ -{{- if .Values.hub.serviceAccount.create -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - {{- with .Values.hub.serviceAccount.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -{{- end }} {{- if .Values.rbac.enabled -}} --- kind: Role diff --git a/jupyterhub/templates/hub/serviceaccount.yaml b/jupyterhub/templates/hub/serviceaccount.yaml new file mode 100644 index 0000000000..f1c3522500 --- /dev/null +++ b/jupyterhub/templates/hub/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.hub.serviceAccount.create -}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + {{- with .Values.hub.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index 78fd68e7a6..2ffec2cddb 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -1,32 +1,9 @@ {{- /* Permissions to be used by the hook-image-awaiter job */}} -{{- if .Values.prePuller.hook.serviceAccount.create }} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} -{{- /* -This service account... -*/ -}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - hub.jupyter.org/deletable: "true" - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "0" - {{- with .Values.prePuller.hook.serviceAccount.annotations }} - {{- . | toYaml | nindent 4 }} - {{- end }} -{{- end }} {{- if .Values.rbac.enabled }} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} --- -{{- /* -... will be used by this role... -*/}} kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml new file mode 100644 index 0000000000..2da482337e --- /dev/null +++ b/jupyterhub/templates/image-puller/serviceaccount.yaml @@ -0,0 +1,22 @@ +{{- /* +ServiceAccount for the pre-puller hook's image-awaiter-job +*/}} +{{- if .Values.prePuller.hook.serviceAccount.create }} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + hub.jupyter.org/deletable: "true" + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" + {{- with .Values.prePuller.hook.serviceAccount.annotations }} + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index e29cf544a5..8138392f64 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -1,15 +1,6 @@ {{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} {{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} {{- if $autoHTTPS -}} -{{- if .Values.proxy.traefik.serviceAccount.create }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -{{- end }} {{- if .Values.rbac.enabled }} --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml new file mode 100644 index 0000000000..d02be0e76b --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- if $autoHTTPS -}} +{{- if .Values.proxy.traefik.serviceAccount.create }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index c91f550e6f..53cb7632b6 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -1,16 +1,4 @@ {{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.scheduling.userScheduler.serviceAccount.create }} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.scheduling.userScheduler.serviceAccount.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -{{- end }} {{- if .Values.rbac.enabled }} --- kind: ClusterRole diff --git a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml new file mode 100644 index 0000000000..8e240d4926 --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.scheduling.userScheduler.enabled -}} +{{- if .Values.scheduling.userScheduler.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.scheduling.userScheduler.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} From 08731a4d758983f4fdae67ed33c2f1f6700286f4 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Thu, 2 Jun 2022 11:54:41 +0200 Subject: [PATCH 339/898] fix: whitespace chomp right before any content rendered in each template --- jupyterhub/templates/image-puller/rbac.yaml | 4 ++-- jupyterhub/templates/image-puller/serviceaccount.yaml | 4 ++-- jupyterhub/templates/proxy/autohttps/rbac.yaml | 6 +++--- jupyterhub/templates/proxy/autohttps/serviceaccount.yaml | 6 +++--- jupyterhub/templates/scheduling/user-scheduler/rbac.yaml | 2 +- .../templates/scheduling/user-scheduler/serviceaccount.yaml | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index 2ffec2cddb..9eb4bb68a0 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -1,8 +1,8 @@ {{- /* Permissions to be used by the hook-image-awaiter job */}} -{{- if .Values.rbac.enabled }} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} +{{- if .Values.rbac.enabled -}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml index 2da482337e..1d5d4e9912 100644 --- a/jupyterhub/templates/image-puller/serviceaccount.yaml +++ b/jupyterhub/templates/image-puller/serviceaccount.yaml @@ -1,8 +1,8 @@ {{- /* ServiceAccount for the pre-puller hook's image-awaiter-job */}} -{{- if .Values.prePuller.hook.serviceAccount.create }} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) }} +{{- if .Values.prePuller.hook.serviceAccount.create -}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} --- apiVersion: v1 kind: ServiceAccount diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index 8138392f64..346b520b94 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -1,7 +1,7 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} {{- if $autoHTTPS -}} -{{- if .Values.rbac.enabled }} +{{- if .Values.rbac.enabled -}} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml index d02be0e76b..8dc4327420 100644 --- a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml +++ b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml @@ -1,7 +1,7 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} {{- if $autoHTTPS -}} -{{- if .Values.proxy.traefik.serviceAccount.create }} +{{- if .Values.proxy.traefik.serviceAccount.create -}} --- apiVersion: v1 kind: ServiceAccount diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 53cb7632b6..96b1de2216 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -1,5 +1,5 @@ {{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.rbac.enabled }} +{{- if .Values.rbac.enabled -}} --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml index 8e240d4926..6e3355383f 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml @@ -1,5 +1,5 @@ {{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.scheduling.userScheduler.serviceAccount.create }} +{{- if .Values.scheduling.userScheduler.serviceAccount.create -}} apiVersion: v1 kind: ServiceAccount metadata: From 07687176e4a44dcb551e14c4f24da0848ec9e4a2 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Thu, 2 Jun 2022 13:54:58 +0200 Subject: [PATCH 340/898] feat: Implement `serviceAccount.name` This allows the user to set the name of the serviceAccounts that are created If no name is given, the default is the deployment (or job) name for the respective account --- jupyterhub/schema.yaml | 8 ++++++++ jupyterhub/templates/_helpers-names.tpl | 20 +++++++++++++++++++ jupyterhub/templates/hub/deployment.yaml | 2 +- jupyterhub/templates/hub/rbac.yaml | 2 +- jupyterhub/templates/hub/serviceaccount.yaml | 2 +- jupyterhub/templates/image-puller/job.yaml | 2 +- jupyterhub/templates/image-puller/rbac.yaml | 2 +- .../image-puller/serviceaccount.yaml | 2 +- .../templates/proxy/autohttps/deployment.yaml | 2 +- .../templates/proxy/autohttps/rbac.yaml | 2 +- .../proxy/autohttps/serviceaccount.yaml | 2 +- .../scheduling/user-scheduler/deployment.yaml | 2 +- .../scheduling/user-scheduler/rbac.yaml | 2 +- .../user-scheduler/serviceaccount.yaml | 2 +- jupyterhub/values.yaml | 4 ++++ 15 files changed, 44 insertions(+), 12 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 1e2829cb24..e90e49b206 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1263,6 +1263,14 @@ properties: type: boolean description: | Whether or not to create the `ServiceAccount` resource + name: + type: ["string", "null"] + description: | + This configuration serves multiple purposes: + - It will be the `serviceAccountName` referenced by related Pods. + - If `create` is set, the created ServiceAccount resource will be named like this. + - If [`rbac.enabled`](schema_rbac.enabled) is set, the associated (Cluster)RoleBindings will bind to this name. + If not explicitly provided, a default name will be used. annotations: type: object additionalProperties: false diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 4d270bf7af..5e6d990e19 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -76,6 +76,11 @@ {{- include "jupyterhub.fullname.dash" . }}hub {{- end }} +{{- /* hub-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.hub-serviceaccount.fullname" -}} + {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" .) }} +{{- end }} + {{- /* hub-existing-secret Secret */}} {{- define "jupyterhub.hub-existing-secret.fullname" -}} {{- /* A hack to avoid issues from invoking this from a parent Helm chart. */}} @@ -133,11 +138,21 @@ {{- include "jupyterhub.fullname.dash" . }}autohttps {{- end }} +{{- /* autohttps-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} + {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" .) }} +{{- end }} + {{- /* user-scheduler Deployment */}} {{- define "jupyterhub.user-scheduler-deploy.fullname" -}} {{- include "jupyterhub.fullname.dash" . }}user-scheduler {{- end }} +{{- /* user-scheduler-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" .) }} +{{- end }} + {{- /* user-scheduler leader election lock resource */}} {{- define "jupyterhub.user-scheduler-lock.fullname" -}} {{- include "jupyterhub.user-scheduler-deploy.fullname" . }}-lock @@ -153,6 +168,11 @@ {{- include "jupyterhub.fullname.dash" . }}hook-image-awaiter {{- end }} +{{- /* image-awaiter-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} + {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" .) }} +{{- end }} + {{- /* hook-image-puller DaemonSet */}} {{- define "jupyterhub.hook-image-puller.fullname" -}} {{- include "jupyterhub.fullname.dash" . }}hook-image-puller diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index c58d9c64f0..f65527f4a3 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -76,7 +76,7 @@ spec: claimName: {{ include "jupyterhub.hub-pvc.fullname" . }} {{- end }} {{- if .Values.hub.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.hub.fullname" . }} + serviceAccountName: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} {{- end }} {{- with .Values.hub.podSecurityContext }} securityContext: diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml index 1d819e03d2..f77a1eae0d 100644 --- a/jupyterhub/templates/hub/rbac.yaml +++ b/jupyterhub/templates/hub/rbac.yaml @@ -22,7 +22,7 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} subjects: - kind: ServiceAccount - name: {{ include "jupyterhub.hub.fullname" . }} + name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} namespace: "{{ .Release.Namespace }}" roleRef: kind: Role diff --git a/jupyterhub/templates/hub/serviceaccount.yaml b/jupyterhub/templates/hub/serviceaccount.yaml index f1c3522500..8aba2d3c7b 100644 --- a/jupyterhub/templates/hub/serviceaccount.yaml +++ b/jupyterhub/templates/hub/serviceaccount.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "jupyterhub.hub.fullname" . }} + name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} {{- with .Values.hub.serviceAccount.annotations }} annotations: {{- . | toYaml | nindent 4 }} diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml index d960adc9a7..eeacbfe6ac 100644 --- a/jupyterhub/templates/image-puller/job.yaml +++ b/jupyterhub/templates/image-puller/job.yaml @@ -35,7 +35,7 @@ spec: spec: restartPolicy: Never {{- if .Values.prePuller.hook.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + serviceAccountName: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} {{- end }} {{- with .Values.prePuller.hook.nodeSelector }} nodeSelector: diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index 9eb4bb68a0..f45701324a 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -36,7 +36,7 @@ metadata: "helm.sh/hook-weight": "0" subjects: - kind: ServiceAccount - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} namespace: "{{ .Release.Namespace }}" roleRef: kind: Role diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml index 1d5d4e9912..3ec698af34 100644 --- a/jupyterhub/templates/image-puller/serviceaccount.yaml +++ b/jupyterhub/templates/image-puller/serviceaccount.yaml @@ -7,7 +7,7 @@ ServiceAccount for the pre-puller hook's image-awaiter-job apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} labels: {{- include "jupyterhub.labels" . | nindent 4 }} hub.jupyter.org/deletable: "true" diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 6efe72af9e..744878f4e7 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -28,7 +28,7 @@ spec: checksum/static-config: {{ include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | sha256sum }} spec: {{- if .Values.proxy.traefik.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.autohttps.fullname" . }} + serviceAccountName: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index 346b520b94..1047bf934f 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -26,7 +26,7 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} subjects: - kind: ServiceAccount - name: {{ include "jupyterhub.autohttps.fullname" . }} + name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} apiGroup: roleRef: kind: Role diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml index 8dc4327420..06645ff135 100644 --- a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml +++ b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml @@ -6,7 +6,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} + name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} labels: {{- include "jupyterhub.labels" . | nindent 4 }} {{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 0e82ad6eed..d0e80cfc82 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -18,7 +18,7 @@ spec: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} spec: {{- if .Values.scheduling.userScheduler.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + serviceAccountName: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 96b1de2216..70fb1ad94b 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -224,7 +224,7 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} subjects: - kind: ServiceAccount - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} namespace: "{{ .Release.Namespace }}" roleRef: kind: ClusterRole diff --git a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml index 6e3355383f..f84ffc1c81 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} labels: {{- include "jupyterhub.labels" . | nindent 4 }} {{- with .Values.scheduling.userScheduler.serviceAccount.annotations }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index e87f064e55..2ec2ccca8b 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -138,6 +138,7 @@ hub: existingSecret: serviceAccount: create: true + name: annotations: {} extraPodSpec: {} @@ -280,6 +281,7 @@ proxy: minAvailable: 1 serviceAccount: create: true + name: annotations: {} extraPodSpec: {} secretSync: @@ -517,6 +519,7 @@ scheduling: resources: {} serviceAccount: create: true + name: annotations: {} extraPodSpec: {} podPriority: @@ -597,6 +600,7 @@ prePuller: resources: {} serviceAccount: create: true + name: annotations: {} continuous: enabled: true From f09f494c0f2941e5fc0065749e9f5577dbc935f0 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Thu, 2 Jun 2022 14:06:58 +0200 Subject: [PATCH 341/898] chore: Add serviceAccount.name and .create to the test values yaml file --- tools/templates/lint-and-validate-values.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 7f5d641e5f..2d4c281948 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -189,6 +189,8 @@ hub: value: mock-taint-value-hub effect: NoSchedule serviceAccount: &serviceAccount + create: true + name: mock-serviceaccount-name annotations: *annotations extraPodSpec: &extraPodSpec hostNetwork: true From 0133a61d129a7cf4b1559af3aebafc10b34f1287 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Fri, 3 Jun 2022 09:23:14 +0200 Subject: [PATCH 342/898] feat: allow referencing a `ServiceAccount` without creating one --- jupyterhub/templates/_helpers-names.tpl | 24 +++++++++++++++---- jupyterhub/templates/hub/deployment.yaml | 4 ++-- jupyterhub/templates/image-puller/job.yaml | 4 ++-- .../templates/proxy/autohttps/deployment.yaml | 4 ++-- .../scheduling/user-scheduler/deployment.yaml | 4 ++-- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 5e6d990e19..9f4ee54cd1 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -78,7 +78,11 @@ {{- /* hub-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hub-serviceaccount.fullname" -}} - {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" .) }} + {{- if .Values.hub.serviceAccount.name }} + {{- .Values.hub.serviceAccount.name }} + {{- else if .Values.hub.serviceAccount.create }} + {{- include "jupyterhub.hub.fullname" . }} + {{- end }} {{- end }} {{- /* hub-existing-secret Secret */}} @@ -140,7 +144,11 @@ {{- /* autohttps-serviceaccount ServiceAccount */}} {{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} - {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" .) }} + {{- if .Values.proxy.traefik.serviceAccount.name }} + {{- .Values.proxy.traefik.serviceAccount.name }} + {{- else if .Values.proxy.traefik.serviceAccount.create }} + {{- include "jupyterhub.autohttps.fullname" . }} + {{- end }} {{- end }} {{- /* user-scheduler Deployment */}} @@ -150,7 +158,11 @@ {{- /* user-scheduler-serviceaccount ServiceAccount */}} {{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} - {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" .) }} + {{- if .Values.scheduling.userScheduler.serviceAccount.name }} + {{- .Values.scheduling.userScheduler.serviceAccount.name }} + {{- else if .Values.scheduling.userScheduler.serviceAccount.create }} + {{- include "jupyterhub.user-scheduler-deploy.fullname" . }} + {{- end }} {{- end }} {{- /* user-scheduler leader election lock resource */}} @@ -170,7 +182,11 @@ {{- /* image-awaiter-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} - {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" .) }} + {{- if .Values.prePuller.hook.serviceAccount.name }} + {{- .Values.prePuller.hook.serviceAccount.name }} + {{- else if .Values.prePuller.hook.serviceAccount.create }} + {{- include "jupyterhub.hook-image-awaiter.fullname" . }} + {{- end }} {{- end }} {{- /* hook-image-puller DaemonSet */}} diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index f65527f4a3..15a8d33a69 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -75,8 +75,8 @@ spec: persistentVolumeClaim: claimName: {{ include "jupyterhub.hub-pvc.fullname" . }} {{- end }} - {{- if .Values.hub.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} + {{- with include "jupyterhub.hub-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} {{- end }} {{- with .Values.hub.podSecurityContext }} securityContext: diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml index eeacbfe6ac..f9c90ce47d 100644 --- a/jupyterhub/templates/image-puller/job.yaml +++ b/jupyterhub/templates/image-puller/job.yaml @@ -34,8 +34,8 @@ spec: {{- end }} spec: restartPolicy: Never - {{- if .Values.prePuller.hook.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} + {{- with include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} {{- end }} {{- with .Values.prePuller.hook.nodeSelector }} nodeSelector: diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 744878f4e7..32d5e93af1 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -27,8 +27,8 @@ spec: # entrypoint of all network traffic. checksum/static-config: {{ include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | sha256sum }} spec: - {{- if .Values.proxy.traefik.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} + {{- with include "jupyterhub.autohttps-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index d0e80cfc82..a5c8695862 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -17,8 +17,8 @@ spec: annotations: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} spec: - {{- if .Values.scheduling.userScheduler.serviceAccount.create }} - serviceAccountName: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} + {{ with include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} {{- end }} {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.priority.fullname" . }} From 02ccaf29664b3c0fe402e96c76c02d033e347c06 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Fri, 3 Jun 2022 09:37:31 +0200 Subject: [PATCH 343/898] chore: remove yaml document start for first document in template --- jupyterhub/templates/hub/rbac.yaml | 1 - jupyterhub/templates/hub/serviceaccount.yaml | 1 - jupyterhub/templates/image-puller/rbac.yaml | 1 - jupyterhub/templates/image-puller/serviceaccount.yaml | 1 - jupyterhub/templates/proxy/autohttps/rbac.yaml | 1 - jupyterhub/templates/proxy/autohttps/serviceaccount.yaml | 1 - jupyterhub/templates/scheduling/user-scheduler/rbac.yaml | 1 - 7 files changed, 7 deletions(-) diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml index f77a1eae0d..81d6efb830 100644 --- a/jupyterhub/templates/hub/rbac.yaml +++ b/jupyterhub/templates/hub/rbac.yaml @@ -1,5 +1,4 @@ {{- if .Values.rbac.enabled -}} ---- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: diff --git a/jupyterhub/templates/hub/serviceaccount.yaml b/jupyterhub/templates/hub/serviceaccount.yaml index 8aba2d3c7b..06a50692a6 100644 --- a/jupyterhub/templates/hub/serviceaccount.yaml +++ b/jupyterhub/templates/hub/serviceaccount.yaml @@ -1,5 +1,4 @@ {{- if .Values.hub.serviceAccount.create -}} ---- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index f45701324a..16c84813e8 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -3,7 +3,6 @@ Permissions to be used by the hook-image-awaiter job */}} {{- if .Values.rbac.enabled -}} {{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} ---- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml index 3ec698af34..8161101885 100644 --- a/jupyterhub/templates/image-puller/serviceaccount.yaml +++ b/jupyterhub/templates/image-puller/serviceaccount.yaml @@ -3,7 +3,6 @@ ServiceAccount for the pre-puller hook's image-awaiter-job */}} {{- if .Values.prePuller.hook.serviceAccount.create -}} {{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} ---- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index 1047bf934f..60a4dd0687 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -2,7 +2,6 @@ {{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} {{- if $autoHTTPS -}} {{- if .Values.rbac.enabled -}} ---- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml index 06645ff135..5b340bbbcd 100644 --- a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml +++ b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml @@ -2,7 +2,6 @@ {{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} {{- if $autoHTTPS -}} {{- if .Values.proxy.traefik.serviceAccount.create -}} ---- apiVersion: v1 kind: ServiceAccount metadata: diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 70fb1ad94b..fa33e67b6b 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -1,6 +1,5 @@ {{- if .Values.scheduling.userScheduler.enabled -}} {{- if .Values.rbac.enabled -}} ---- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: From 464a3196141d1b4e88f037946d59d45b5216ec0e Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Fri, 3 Jun 2022 09:48:57 +0200 Subject: [PATCH 344/898] fix: add missing serviceaccount names to the fullnames list --- jupyterhub/templates/_helpers-names.tpl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 9f4ee54cd1..a39cb66c09 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -276,6 +276,7 @@ fullname: {{ include "jupyterhub.fullname" . | quote }} fullname-dash: {{ include "jupyterhub.fullname.dash" . | quote }} hub: {{ include "jupyterhub.hub.fullname" . | quote }} +hub-serviceaccount: {{ include "jupyterhub.hub-serviceaccount.fullname" . | quote }} hub-existing-secret: {{ include "jupyterhub.hub-existing-secret.fullname" . | quote }} hub-existing-secret-or-default: {{ include "jupyterhub.hub-existing-secret-or-default.fullname" . | quote }} hub-pvc: {{ include "jupyterhub.hub-pvc.fullname" . | quote }} @@ -286,11 +287,14 @@ proxy-public: {{ include "jupyterhub.proxy-public.fullname" . | quote }} proxy-public-tls: {{ include "jupyterhub.proxy-public-tls.fullname" . | quote }} proxy-public-manual-tls: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . | quote }} autohttps: {{ include "jupyterhub.autohttps.fullname" . | quote }} +autohttps-serviceaccount: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . | quote }} user-scheduler-deploy: {{ include "jupyterhub.user-scheduler-deploy.fullname" . | quote }} +user-scheduler-serviceaccount: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . | quote }} user-scheduler-lock: {{ include "jupyterhub.user-scheduler-lock.fullname" . | quote }} user-placeholder: {{ include "jupyterhub.user-placeholder.fullname" . | quote }} image-puller-priority: {{ include "jupyterhub.image-puller-priority.fullname" . | quote }} hook-image-awaiter: {{ include "jupyterhub.hook-image-awaiter.fullname" . | quote }} +hook-image-awaiter-serviceaccount: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . | quote }} hook-image-puller: {{ include "jupyterhub.hook-image-puller.fullname" . | quote }} continuous-image-puller: {{ include "jupyterhub.continuous-image-puller.fullname" . | quote }} singleuser: {{ include "jupyterhub.singleuser.fullname" . | quote }} From cd68204087a59699b099e370c78b7dfd7a6de431 Mon Sep 17 00:00:00 2001 From: Martin Morset Date: Fri, 3 Jun 2022 14:45:35 +0200 Subject: [PATCH 345/898] feat: set serviceaccount references in pods no matter if no serviceAccount.name is provided --- jupyterhub/templates/_helpers-names.tpl | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index a39cb66c09..6737482b5f 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -78,10 +78,10 @@ {{- /* hub-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hub-serviceaccount.fullname" -}} - {{- if .Values.hub.serviceAccount.name }} - {{- .Values.hub.serviceAccount.name }} - {{- else if .Values.hub.serviceAccount.create }} - {{- include "jupyterhub.hub.fullname" . }} + {{- if .Values.hub.serviceAccount.create }} + {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" . ) }} + {{- else }} + {{- .Values.hub.serviceAccount.name | default "default"}} {{- end }} {{- end }} @@ -144,10 +144,10 @@ {{- /* autohttps-serviceaccount ServiceAccount */}} {{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} - {{- if .Values.proxy.traefik.serviceAccount.name }} - {{- .Values.proxy.traefik.serviceAccount.name }} - {{- else if .Values.proxy.traefik.serviceAccount.create }} - {{- include "jupyterhub.autohttps.fullname" . }} + {{- if .Values.proxy.traefik.serviceAccount.create }} + {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" . ) }} + {{- else }} + {{- .Values.proxy.traefik.serviceAccount.name | default "default"}} {{- end }} {{- end }} @@ -158,10 +158,10 @@ {{- /* user-scheduler-serviceaccount ServiceAccount */}} {{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} - {{- if .Values.scheduling.userScheduler.serviceAccount.name }} - {{- .Values.scheduling.userScheduler.serviceAccount.name }} - {{- else if .Values.scheduling.userScheduler.serviceAccount.create }} - {{- include "jupyterhub.user-scheduler-deploy.fullname" . }} + {{- if .Values.scheduling.userScheduler.serviceAccount.create }} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" . ) }} + {{- else }} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default "default"}} {{- end }} {{- end }} @@ -182,10 +182,10 @@ {{- /* image-awaiter-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} - {{- if .Values.prePuller.hook.serviceAccount.name }} - {{- .Values.prePuller.hook.serviceAccount.name }} - {{- else if .Values.prePuller.hook.serviceAccount.create }} - {{- include "jupyterhub.hook-image-awaiter.fullname" . }} + {{- if .Values.prePuller.hook.serviceAccount.create }} + {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" . ) }} + {{- else }} + {{- .Values.prePuller.hook.serviceAccount.name | default "default"}} {{- end }} {{- end }} From 211bd8b343b75fd35fa1ea495ca1e896f3176809 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 15:05:00 +0200 Subject: [PATCH 346/898] Small formatting adjustments --- jupyterhub/templates/_helpers-names.tpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 6737482b5f..bbb5864e28 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -79,9 +79,9 @@ {{- /* hub-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hub-serviceaccount.fullname" -}} {{- if .Values.hub.serviceAccount.create }} - {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" . ) }} + {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" .) }} {{- else }} - {{- .Values.hub.serviceAccount.name | default "default"}} + {{- .Values.hub.serviceAccount.name | default "default" }} {{- end }} {{- end }} @@ -145,9 +145,9 @@ {{- /* autohttps-serviceaccount ServiceAccount */}} {{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} {{- if .Values.proxy.traefik.serviceAccount.create }} - {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" . ) }} + {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" .) }} {{- else }} - {{- .Values.proxy.traefik.serviceAccount.name | default "default"}} + {{- .Values.proxy.traefik.serviceAccount.name | default "default" }} {{- end }} {{- end }} @@ -159,9 +159,9 @@ {{- /* user-scheduler-serviceaccount ServiceAccount */}} {{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} {{- if .Values.scheduling.userScheduler.serviceAccount.create }} - {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" . ) }} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" .) }} {{- else }} - {{- .Values.scheduling.userScheduler.serviceAccount.name | default "default"}} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default "default" }} {{- end }} {{- end }} @@ -183,9 +183,9 @@ {{- /* image-awaiter-serviceaccount ServiceAccount */}} {{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} {{- if .Values.prePuller.hook.serviceAccount.create }} - {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" . ) }} + {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" .) }} {{- else }} - {{- .Values.prePuller.hook.serviceAccount.name | default "default"}} + {{- .Values.prePuller.hook.serviceAccount.name | default "default" }} {{- end }} {{- end }} From e0e1f75cfdf4ad9ca6dd9eefd077a5ef2850a854 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 16:12:13 +0200 Subject: [PATCH 347/898] docs: fix typo --- docs/source/administrator/security.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 60460ed4b7..f6c922fe70 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -197,7 +197,7 @@ kubectl --namespace=kube-system delete rc kubernetes-dashboard Kubernetes supports, and often requires, using [Role Based Access Control (RBAC)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) to secure which pods / users can perform what kinds of actions on the cluster. RBAC rules can be set to provide users with minimal necessary access based on their administrative needs. -It is **critical** to understand that if RBAC is disabled, all pods are given `root` equivalent permission on the Kubernetes cluster and all the nodes in it. This opens up very bad vulnerabilites for your security. +It is **critical** to understand that if RBAC is disabled, all pods are given `root` equivalent permission on the Kubernetes cluster and all the nodes in it. This opens up very bad vulnerabilities for your security. As of the Helm chart v0.5 used with JupyterHub and BinderHub, the helm chart can natively work with RBAC enabled clusters. To provide sensible security defaults, we ship appropriate minimal RBAC rules for the various components we use. We **highly recommend** using these minimal or more restrictive RBAC rules. From d4298bd821ad233e84d6ca20d8880243fb2a1919 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Jun 2022 16:12:57 +0200 Subject: [PATCH 348/898] breaking, maint: replace rbac.enabled with rbac.create --- docs/source/administrator/security.md | 2 +- jupyterhub/schema.yaml | 43 ++++++++++++++++--- jupyterhub/templates/NOTES.txt | 5 +++ jupyterhub/templates/_helpers.tpl | 2 +- jupyterhub/templates/hub/rbac.yaml | 2 +- jupyterhub/templates/image-puller/rbac.yaml | 2 +- .../templates/proxy/autohttps/rbac.yaml | 2 +- .../scheduling/user-scheduler/rbac.yaml | 2 +- jupyterhub/values.yaml | 2 +- tools/templates/lint-and-validate-values.yaml | 2 +- 10 files changed, 51 insertions(+), 13 deletions(-) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index f6c922fe70..3cbbfa7964 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -205,7 +205,7 @@ If you want to disable the RBAC rules, for whatever reason, you can do so with t ```yaml rbac: - enabled: false + create: false ``` We strongly **discourage disabling** the RBAC rules and remind you that this diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index d23ff76583..4b5d4d9fc0 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1366,6 +1366,7 @@ properties: description: *jupyterhub-native-config-description serviceAccount: &serviceAccount type: object + required: [create] additionalProperties: false description: | Configuration for a k8s ServiceAccount dedicated for use by the @@ -1374,14 +1375,16 @@ properties: create: type: boolean description: | - Whether or not to create the `ServiceAccount` resource + Whether or not to create the `ServiceAccount` resource. name: type: ["string", "null"] description: | This configuration serves multiple purposes: + - It will be the `serviceAccountName` referenced by related Pods. - If `create` is set, the created ServiceAccount resource will be named like this. - - If [`rbac.enabled`](schema_rbac.enabled) is set, the associated (Cluster)RoleBindings will bind to this name. + - If [`rbac.create`](schema_rbac.create) is set, the associated (Cluster)RoleBindings will bind to this name. + If not explicitly provided, a default name will be used. annotations: type: object @@ -2837,13 +2840,43 @@ properties: rbac: type: object additionalProperties: false - required: [enabled] + required: [create] properties: enabled: + type: boolean + # This schema entry is needed to help us print a more helpful error + # message in NOTES.txt if hub.fsGid is set. + # + description: | + ````{note} + Removed in version 2.0.0. If you have been using `rbac.enable=false` + (strongly discouraged), then the equivalent configuration would be: + + ```yaml + rbac: + create: false + hub: + serviceAccount: + create: false + proxy: + traefik: + serviceAccount: + create: false + scheduling: + userScheduler: + serviceAccount: + create: false + prePuller: + hook: + serviceAccount: + create: false + ``` + ```` + create: type: boolean description: | - Decides if RBAC resources are to be created and referenced by the the - Helm chart's workloads. + Decides if (Cluster)Role and (Cluster)RoleBinding resources are + created and bound to the configured serviceAccounts. global: type: object diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index 01caee73d4..e7509ff6e8 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -138,6 +138,11 @@ */}} +{{- if hasKey .Values.rbac "enabled" }} +{{- $breaking = print $breaking "\n\nCHANGED: rbac.enabled must as of version 2.0.0 be configured via rbac.create and .serviceAccount.create." }} +{{- end }} + + {{- if hasKey .Values.hub "fsGid" }} {{- $breaking = print $breaking "\n\nCHANGED: hub.fsGid must as of version 2.0.0 be configured via hub.podSecurityContext.fsGroup." }} {{- end }} diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl index eecdc8995b..88b24a1fea 100644 --- a/jupyterhub/templates/_helpers.tpl +++ b/jupyterhub/templates/_helpers.tpl @@ -12,7 +12,7 @@ When you ask a helper to render its content, one often forward the current scope to the helper in order to allow it to access .Release.Name, - .Values.rbac.enabled and similar values. + .Values.rbac.create and similar values. #### Example - Passing the current scope {{ include "jupyterhub.commonLabels" . }} diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml index 81d6efb830..3abf00eb5e 100644 --- a/jupyterhub/templates/hub/rbac.yaml +++ b/jupyterhub/templates/hub/rbac.yaml @@ -1,4 +1,4 @@ -{{- if .Values.rbac.enabled -}} +{{- if .Values.rbac.create -}} kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml index 16c84813e8..996a59a4a3 100644 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -1,7 +1,7 @@ {{- /* Permissions to be used by the hook-image-awaiter job */}} -{{- if .Values.rbac.enabled -}} +{{- if .Values.rbac.create -}} {{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} kind: Role apiVersion: rbac.authorization.k8s.io/v1 diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml index 60a4dd0687..a0fd41aefa 100644 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -1,7 +1,7 @@ {{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} {{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} {{- if $autoHTTPS -}} -{{- if .Values.rbac.enabled -}} +{{- if .Values.rbac.create -}} apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index fa33e67b6b..f77640b146 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -1,5 +1,5 @@ {{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.rbac.enabled -}} +{{- if .Values.rbac.create -}} kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 90cec66237..b07ec6a39d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -139,7 +139,7 @@ hub: extraPodSpec: {} rbac: - enabled: true + create: true # proxy relates to the proxy pod, the proxy-public service, and the autohttps # pod and proxy-http service. diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 0f35096c80..e60717ae84 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -206,7 +206,7 @@ hub: dnsPolicy: ClusterFirstWithHostNet rbac: - enabled: true + create: true proxy: service: From 9cb726481cd2799934fbefbf8b3876dac3cf62b8 Mon Sep 17 00:00:00 2001 From: James McBride Date: Fri, 3 Jun 2022 17:54:59 -0700 Subject: [PATCH 349/898] Fix link to authentication guide The current link results in a 404, it looks like is just missing the `.md` extension. --- docs/source/jupyterhub/customizing/user-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-management.md b/docs/source/jupyterhub/customizing/user-management.md index 1818d98068..468bbc5b9d 100644 --- a/docs/source/jupyterhub/customizing/user-management.md +++ b/docs/source/jupyterhub/customizing/user-management.md @@ -114,4 +114,4 @@ hub: ## Authenticating Users For information on authenticating users in JupyterHub, see -[the Authentication guide](../../administrator/authentication). +[the Authentication guide](../../administrator/authentication.md). From a6eac399a9ca6a2e5ef9188f34d1df4d955f137e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 5 Jun 2022 11:53:26 +0200 Subject: [PATCH 350/898] docs: update notes about building wheels in build stage --- images/hub/Dockerfile | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index eaf31a5745..22d0771fd4 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -6,9 +6,21 @@ FROM python:3.9-bullseye as build-stage WORKDIR /build-stage -# Build wheels for packages that requires gcc and other build dependencies and -# lack wheels either for amd64 or aarch64. If you find a dependency here no -# longer listed in requirements.txt, remove it from here as well. +# Build wheels for packages that require gcc or other build dependencies and +# lack wheels either for amd64 or aarch64. +# +# - https://pypi.org/project/pycryptodomex/#files, no aarch64 wheels available +# as of 3.14.1. See https://github.com/Legrandin/pycryptodome/issues/628. +# - https://pypi.org/project/pycurl/#files, no wheels available as of 7.45.1. +# See https://github.com/pycurl/pycurl/issues/738. +# - https://pypi.org/project/ruamel.yaml.clib/#files, no aarch64 wheels +# available as of 0.2.6. See +# https://sourceforge.net/p/ruamel-yaml-clib/tickets/4/. +# +# If you find a dependency here no longer listed in requirements.txt, remove it +# from here as well. The more wheels we build here and copy and then install, +# where the copy and install are separate steps, the larger the final image +# becomes. # COPY requirements.txt requirements.txt RUN pip install build \ From 4b9ce52a653ebb8f6681055419aec3ce3ba78446 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 5 Jun 2022 11:56:29 +0200 Subject: [PATCH 351/898] hub image: downgrade to ltiauthenticator 1.2.0 --- images/hub/requirements.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index b27d3ccf96..1a12cf5996 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -10,7 +10,7 @@ jupyterhub==2.3.0 jupyterhub-firstuseauthenticator>=1 jupyterhub-hmacauthenticator jupyterhub-ldapauthenticator -jupyterhub-ltiauthenticator +jupyterhub-ltiauthenticator!=1.3.0 jupyterhub-nativeauthenticator jupyterhub-tmpauthenticator nullauthenticator From c6c8a74442f75bd2d5ab64d043cd90bec7295252 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 5 Jun 2022 12:02:04 +0200 Subject: [PATCH 352/898] hub image: refreeze dependencies --- images/hub/requirements.txt | 46 ++++++++----------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index f4dce5a48e..41fde6e05d 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.1 # via kubernetes-asyncio aiosignal==1.2.0 # via aiohttp -alembic==1.7.7 +alembic==1.8.0 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -37,12 +37,7 @@ charset-normalizer==2.0.12 # aiohttp # requests cryptography==37.0.2 - # via - # josepy - # jwcrypto - # pyopenssl -deprecated==1.2.13 - # via jwcrypto + # via pyopenssl entrypoints==0.4 # via jupyterhub escapism==1.0.1 @@ -53,8 +48,6 @@ frozenlist==1.3.0 # via # aiohttp # aiosignal -future==0.18.2 - # via pyjwkest greenlet==1.1.2 # via sqlalchemy idna==3.3 @@ -65,9 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -josepy==1.13.0 - # via jupyterhub-ltiauthenticator -jsonschema==4.5.1 +jsonschema==4.6.0 # via jupyter-telemetry jupyter-telemetry==0.1.0 # via jupyterhub @@ -91,14 +82,12 @@ jupyterhub-kubespawner==4.1.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.3.0 +jupyterhub-ltiauthenticator==1.2.0 # via -r requirements.in jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -jwcrypto==1.3.1 - # via jupyterhub-ltiauthenticator kubernetes-asyncio==22.6.5 # via jupyterhub-kubespawner ldap3==2.9.1 @@ -118,9 +107,7 @@ mwoauth==0.3.8 nullauthenticator==1.0.0 # via -r requirements.in oauthenticator==14.2.0 - # via - # -r requirements.in - # jupyterhub-ltiauthenticator + # via -r requirements.in oauthlib==3.2.0 # via # jupyterhub @@ -133,8 +120,6 @@ packaging==21.3 # via jupyterhub pamela==1.0.0 # via jupyterhub -pem==21.2.0 - # via jupyterhub-ltiauthenticator prometheus-client==0.14.1 # via jupyterhub psycopg2-binary==2.9.3 @@ -145,25 +130,16 @@ pyasn1==0.4.8 # via ldap3 pycparser==2.21 # via cffi -pycryptodome==3.14.1 - # via jupyterhub-ltiauthenticator -pycryptodomex==3.14.1 - # via pyjwkest pycurl==7.45.1 # via -r requirements.in -pyjwkest==1.4.2 - # via jupyterhub-ltiauthenticator -pyjwt==1.7.1 +pyjwt==2.4.0 # via # -r requirements.in - # jupyterhub-ltiauthenticator # mwoauth pymysql==1.0.2 # via -r requirements.in pyopenssl==22.0.0 - # via - # certipy - # josepy + # via certipy pyparsing==3.0.9 # via packaging pyrsistent==0.18.1 @@ -185,7 +161,6 @@ requests==2.27.1 # via # jupyterhub # mwoauth - # pyjwkest # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth @@ -198,9 +173,8 @@ six==1.16.0 # kubernetes-asyncio # mwoauth # onetimepass - # pyjwkest # python-dateutil -sqlalchemy==1.4.36 +sqlalchemy==1.4.37 # via # alembic # jupyterhub @@ -216,7 +190,7 @@ tornado==6.1 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.2.1.post0 +traitlets==5.2.2.post1 # via # jupyter-telemetry # jupyterhub @@ -226,8 +200,6 @@ urllib3==1.26.9 # jupyterhub-kubespawner # kubernetes-asyncio # requests -wrapt==1.14.1 - # via deprecated yarl==1.7.2 # via aiohttp From 69c13326bf76fc816fccffc4f64c898bfe1d340a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Jun 2022 05:04:06 +0000 Subject: [PATCH 353/898] build(deps): bump peter-evans/create-pull-request from 4.0.3 to 4.0.4 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.3 to 4.0.4. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/v4.0.3...923ad837f191474af6b1721408744feb989a4c27) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 332011d1a3..d86a27b7ca 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -190,7 +190,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@f094b77505fb89581e68a1163fbd2fffece39da1 + uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 1fee575247..6dc701a7da 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -104,7 +104,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@v4.0.3 + uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -170,7 +170,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@v4.0.3 + uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 455ae5161418afb7bd3124e92bd1087c88ec13a3 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 6 Jun 2022 14:33:11 +0000 Subject: [PATCH 354/898] Update jupyterhub from 2.3.0 to 2.3.1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 13 +++++++++---- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 1a12cf5996..cd155a866d 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -4,7 +4,7 @@ # # JupyterHub itself -jupyterhub==2.3.0 +jupyterhub==2.3.1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 41fde6e05d..2b2faa15f9 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -59,10 +59,12 @@ jinja2==3.1.2 # jupyterhub # jupyterhub-kubespawner jsonschema==4.6.0 - # via jupyter-telemetry + # via + # jupyter-telemetry + # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.3.0 +jupyterhub==2.3.1 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -106,7 +108,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==14.2.0 +oauthenticator==15.0.0 # via -r requirements.in oauthlib==3.2.0 # via @@ -161,11 +163,14 @@ requests==2.27.1 # via # jupyterhub # mwoauth + # oauthenticator # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth ruamel-yaml==0.17.21 - # via jupyter-telemetry + # via + # jupyter-telemetry + # oauthenticator ruamel-yaml-clib==0.2.6 # via ruamel-yaml six==1.16.0 diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index cb19bb20d1..47b40a2e39 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.3.0 +jupyterhub==2.3.1 nbgitpuller==1.1.0 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index e627268b45..e30dfcfa80 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "2.3.0" +appVersion: "2.3.1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From f25e8f00ced86cc912ec510bba832ec2febabd04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jun 2022 05:04:45 +0000 Subject: [PATCH 355/898] build(deps): bump oauthenticator from 15.0.0 to 15.0.1 in /images/hub Bumps [oauthenticator](https://github.com/jupyterhub/oauthenticator) from 15.0.0 to 15.0.1. - [Release notes](https://github.com/jupyterhub/oauthenticator/releases) - [Changelog](https://github.com/jupyterhub/oauthenticator/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterhub/oauthenticator/compare/15.0.0...15.0.1) --- updated-dependencies: - dependency-name: oauthenticator dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 2b2faa15f9..0182904bb5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -108,7 +108,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==15.0.0 +oauthenticator==15.0.1 # via -r requirements.in oauthlib==3.2.0 # via From 98c690b1f75ca6f77529d51485d07b318e46ece5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 12 Jun 2022 11:09:41 +0200 Subject: [PATCH 356/898] ci: add a refreeze requirements.txt job and use dedicated gha env --- .github/workflows/vuln-scan.yaml | 12 ++++ .github/workflows/watch-dependencies.yaml | 77 ++++++++++++++++++----- images/hub/README.md | 20 +----- 3 files changed, 76 insertions(+), 33 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index d86a27b7ca..bc9c38e650 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -6,12 +6,24 @@ # workflow fail to signal that unless we make an exception, which we do for the # singleuser-sample image only. # +# +# About environment: watch-dependencies +# +# To reduce the exposure of the secrets.jupyterhub_bot_pat token that was setup +# for the environment watch-dependencies, we have setup a dedicated environment +# according to steps in +# https://github.com/jupyterhub/team-compass/issues/516#issuecomment-1129961954. +# name: Vuln. scan on: pull_request: paths: - ".github/workflows/vuln-scan.yaml" + push: + paths: + - ".github/workflows/vuln-scan.yaml" + branches: ["main"] schedule: # At 05:00 on Monday - https://crontab.guru - cron: "0 5 * * 1" diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 6dc701a7da..1af97b644a 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -8,12 +8,23 @@ # latest jupyterhub version available on PyPI, and if doing this, also # refreeze images/hub/requirements.txt. # +# About environment: watch-dependencies +# +# To reduce the exposure of the secrets.jupyterhub_bot_pat token that was setup +# for the environment watch-dependencies, we have setup a dedicated environment +# according to steps in +# https://github.com/jupyterhub/team-compass/issues/516#issuecomment-1129961954. +# name: Watch dependencies on: push: paths: + - "images/hub/Dockerfile" + - "images/hub/requirements.in" + - "images/hub/requirements.txt" - ".github/workflows/watch-dependencies.yaml" + branches: ["main"] schedule: # Run at 05:00 every day, ref: https://crontab.guru/#0_5_*_*_* - cron: "0 5 * * *" @@ -24,6 +35,7 @@ jobs: # Don't run this job on forks if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 + environment: watch-dependencies strategy: fail-fast: false @@ -121,6 +133,7 @@ jobs: # Don't run this job on forks if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 + environment: watch-dependencies steps: - uses: actions/checkout@v3 @@ -148,21 +161,6 @@ jobs: sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml - - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in - if: steps.local.outputs.version != steps.latest.outputs.version - # NOTE: If you make an update to this script, also update the - # images/hub/README.md file. - # - run: | - cd images/hub - docker run --rm \ - --env=CUSTOM_COMPILE_COMMAND="see README.md" \ - --volume=$PWD:/io \ - --workdir=/io \ - --user=root \ - python:3.9-bullseye \ - sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' - - name: git diff if: steps.local.outputs.version != steps.latest.outputs.version run: git --no-pager diff --color=always @@ -182,3 +180,52 @@ jobs: body: >- A new jupyterhub version has been detected, version `${{ steps.latest.outputs.version }}`. + + refreeze-dockerfile-requirements-txt: + # Don't run this job on forks, but also not on the daily schedule to reduce + # noise. If we could run this weekly that would be reasonable, but updating + # these dependencies every day is too much noise. + # + if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' && github.event_name != 'schedule' + runs-on: ubuntu-latest + environment: watch-dependencies + + steps: + - uses: actions/checkout@v3 + + - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in + # Because `pip-compile` resolves `requirements.txt` with the current + # Python for the current platform, it should be run on the same Python + # version and platform as our Dockerfile. + # + # Note that as of 2022-05-29, `pip-compile` has issues with `pycurl`, + # but we workaround them by by omitting the `-slim` part from the image + # in the command below. + # + run: | + cd images/hub + docker run --rm \ + --env=CUSTOM_COMPILE_COMMAND='Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml' \ + --volume=$PWD:/io \ + --workdir=/io \ + --user=root \ + python:3.9-bullseye \ + sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + + - name: git diff + run: git --no-pager diff --color=always + + # ref: https://github.com/peter-evans/create-pull-request + - name: Create a PR + uses: peter-evans/create-pull-request@v4 + with: + token: "${{ secrets.jupyterhub_bot_pat }}" + author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> + committer: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> + branch: update-image-requirements + labels: dependencies + commit-message: "hub image: refreeze requirements.txt" + title: "hub image: refreeze requirements.txt" + body: >- + The hub image's requirements.txt has been refrozen based on + requirements.in. diff --git a/images/hub/README.md b/images/hub/README.md index 188f1fece0..4dd81fae13 100644 --- a/images/hub/README.md +++ b/images/hub/README.md @@ -7,21 +7,5 @@ requirements.in file using [`pip-compile`](https://pip-tools.readthedocs.io). ## How to update requirements.txt -Because `pip-compile` resolves `requirements.txt` with the current Python for -the current platform, it should be run on the same Python version and platform -as our Dockerfile. - -Note that as of 2022-05-29, `pip-compile` has issues with `pycurl`, but we -workaround them by by omitting the `-slim` part from the image in the command -below. - -```shell -# update requirements.txt based on requirements.in -docker run --rm \ - --env=CUSTOM_COMPILE_COMMAND="see README.md" \ - --volume=$PWD:/io \ - --workdir=/io \ - --user=root \ - python:3.9-bullseye \ - sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' -``` +Use the "Run workflow" button at +https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml. From e76e1e57580f458b65e29daf4da4ffc377820080 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sun, 12 Jun 2022 09:20:34 +0000 Subject: [PATCH 357/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 0182904bb5..a422c4abec 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with python 3.9 # To update, run: # -# see README.md +# Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # aiohttp==3.8.1 # via kubernetes-asyncio @@ -159,7 +159,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.27.1 +requests==2.28.0 # via # jupyterhub # mwoauth From b23903fe05a1142e9bb54a7bdd7d787cfae8c6ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Jun 2022 14:47:18 +0000 Subject: [PATCH 358/898] build(deps): bump actions/setup-python from 3 to 4 Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 4. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 10 +++++----- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cdf0569bd0..bc43904e0e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -42,7 +42,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 8fa4f56a3c..6453319b22 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 - name: Install dependencies run: pip install pre-commit @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" @@ -76,7 +76,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" @@ -198,9 +198,9 @@ jobs: traefik-enabled: false docker-enabled: true - # NOTE: actions/setup-python@v3 make use of a cache within the GitHub base + # NOTE: actions/setup-python@v4 make use of a cache within the GitHub base # environment and setup in a fraction of a second. - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index c32a933934..ea529a5d63 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -37,7 +37,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index fee7b362ae..81b7bc52c1 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -32,7 +32,7 @@ jobs: # chartpress, used by docs/conf.py, requires git history to set # chart version and image tags correctly fetch-depth: 0 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index bc9c38e650..2e41e28c59 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -56,7 +56,7 @@ jobs: # correctly fetch-depth: 0 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 with: python-version: "3.8" diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 1af97b644a..c463890b51 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -137,7 +137,7 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/setup-python@v4 - name: Get images/hub/requirements.in pinned version of jupyterhub id: local From a63938b1afff00713c80b0ff6b122363fffc88b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Jun 2022 14:47:22 +0000 Subject: [PATCH 359/898] build(deps): bump jupyterhub/action-k3s-helm from 1 to 2 Bumps [jupyterhub/action-k3s-helm](https://github.com/jupyterhub/action-k3s-helm) from 1 to 2. - [Release notes](https://github.com/jupyterhub/action-k3s-helm/releases) - [Changelog](https://github.com/jupyterhub/action-k3s-helm/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/action-k3s-helm/compare/v1...v2) --- updated-dependencies: - dependency-name: jupyterhub/action-k3s-helm dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 8fa4f56a3c..46cecef0eb 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -191,7 +191,7 @@ jobs: # kubectl and helm # # ref: https://github.com/jupyterhub/action-k3s-helm/ - - uses: jupyterhub/action-k3s-helm@v1 + - uses: jupyterhub/action-k3s-helm@v2 with: k3s-channel: ${{ matrix.k3s-channel }} metrics-enabled: false From 924941c3a4efd58c2026cf28115ebd40b4fd8d21 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 12 Jun 2022 17:47:21 +0200 Subject: [PATCH 360/898] debug --- .github/workflows/test-chart.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 46cecef0eb..92debe401c 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -332,4 +332,5 @@ jobs: uses: jupyterhub/action-k8s-namespace-report@v1 if: always() with: - important-workloads: deploy/hub deploy/proxy + namespace: kube-system + important-workloads: deployment/calico-kube-controllers From 5d33d85504d5fe00d6dda6b6eaa27d5f836aeedf Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 12 Jun 2022 19:19:51 +0200 Subject: [PATCH 361/898] ci: specify python-version epxlicitly, use 3.9 --- .github/workflows/test-chart.yaml | 10 +++++----- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 2 ++ 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 6453319b22..c9cc1764cd 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -32,6 +32,8 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 + with: + python-version: "3.9" - name: Install dependencies run: pip install pre-commit @@ -45,7 +47,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Install dependencies run: pip install chartpress yamllint @@ -78,7 +80,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Install dependencies run: | @@ -198,11 +200,9 @@ jobs: traefik-enabled: false docker-enabled: true - # NOTE: actions/setup-python@v4 make use of a cache within the GitHub base - # environment and setup in a fraction of a second. - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" # Install a local ACME server to fill the role of Let's Encrypt (LE). We # do this as the HTTP challenge sent out by an ACME server must be able to diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index ea529a5d63..a7f835da4c 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Install chartpress run: pip install chartpress diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 81b7bc52c1..23ee030345 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -34,7 +34,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Install deps run: pip install -r docs/requirements.txt diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 2e41e28c59..6c6689397b 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -58,7 +58,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.9" - name: Install chartpress run: | diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index c463890b51..0bd5f244ee 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -138,6 +138,8 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 + with: + python-version: "3.9" - name: Get images/hub/requirements.in pinned version of jupyterhub id: local From c54a2a41c3749fb8e327efb86f03ab81f9f4507d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 12 Jun 2022 23:50:48 +0200 Subject: [PATCH 362/898] ci: revert mistakenly added temp debugging change --- .github/workflows/test-chart.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index cc2727de5f..9ef76846c0 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -332,5 +332,4 @@ jobs: uses: jupyterhub/action-k8s-namespace-report@v1 if: always() with: - namespace: kube-system - important-workloads: deployment/calico-kube-controllers + important-workloads: deploy/hub deploy/proxy From ea20dc3ae3c6918f74156ef263429769a70ff673 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 13 Jun 2022 14:53:42 +0200 Subject: [PATCH 363/898] ci: fix permissions for vuln scan workflow --- .github/workflows/vuln-scan.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 6c6689397b..b1bc756c73 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -33,6 +33,7 @@ jobs: trivy_image_scan: if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' runs-on: ubuntu-20.04 + environment: watch-dependencies strategy: fail-fast: false From 46cc50358b75a48ad83e6850ce732a6319002763 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 13 Jun 2022 13:05:48 +0000 Subject: [PATCH 364/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 5f58ed3617..3edb5ce1a0 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-05-30_05:27:31 +# VULN_SCAN_TIME=2022-06-13_13:05:46 USER root RUN apt-get update \ From 4af900a72be1635af61a2037bf149577f7d0a475 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 21:59:40 +0000 Subject: [PATCH 365/898] build(deps): bump jupyterhub/action-k3s-helm from 2 to 3 Bumps [jupyterhub/action-k3s-helm](https://github.com/jupyterhub/action-k3s-helm) from 2 to 3. - [Release notes](https://github.com/jupyterhub/action-k3s-helm/releases) - [Changelog](https://github.com/jupyterhub/action-k3s-helm/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/action-k3s-helm/compare/v2...v3) --- updated-dependencies: - dependency-name: jupyterhub/action-k3s-helm dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 9ef76846c0..5a88e73293 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -193,7 +193,7 @@ jobs: # kubectl and helm # # ref: https://github.com/jupyterhub/action-k3s-helm/ - - uses: jupyterhub/action-k3s-helm@v2 + - uses: jupyterhub/action-k3s-helm@v3 with: k3s-channel: ${{ matrix.k3s-channel }} metrics-enabled: false From a154ac65c095948cd38cbbac55496f00e0e5d13b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Jun 2022 22:29:56 +0000 Subject: [PATCH 366/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.32.1 → v2.34.0](https://github.com/asottile/pyupgrade/compare/v2.32.1...v2.34.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c21ad70c7..d01523e22d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + rev: v2.34.0 hooks: - id: pyupgrade args: From cda1b211a76cf0219a2563de26399b754d3e39d5 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 14 Jun 2022 05:25:13 +0000 Subject: [PATCH 367/898] Update library/traefik version from v2.7.0 to v2.7.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c48b055bd5..17b960eea3 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -246,7 +246,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.7.0" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.7.1" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 4694cca8efea71760288928745a6adadb4bc18ce Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 17 Jun 2022 05:13:38 +0000 Subject: [PATCH 368/898] Update kube-scheduler version from v1.23.7 to v1.23.8 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 17b960eea3..89110dde41 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -501,7 +501,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.7" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.8" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From fc6c3ee7ba3001aacc39638fe8f9c5b53a0abe8c Mon Sep 17 00:00:00 2001 From: Jon Cotton Date: Fri, 17 Jun 2022 11:57:52 -0600 Subject: [PATCH 369/898] Helm chart feature to annotate the user-placeholder pods --- jupyterhub/schema.yaml | 6 ++++++ .../templates/scheduling/user-placeholder/statefulset.yaml | 4 ++++ jupyterhub/values.yaml | 1 + 3 files changed, 11 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 2ad3ee25aa..66727783de 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2509,6 +2509,12 @@ properties: type: integer description: | How many placeholder pods would you like to have? + annotations: + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Extra annotations to add to the placeholder pods. resources: type: object additionalProperties: true diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index 9bc7150255..739f445b11 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -23,6 +23,10 @@ spec: serviceName: {{ include "jupyterhub.user-placeholder.fullname" . }} template: metadata: + {{- if .Values.scheduling.userPlaceholder.annotations }} + annotations: + {{- toYaml .Values.scheduling.userPlaceholder.annotations | nindent 8 }} + {{- end }} labels: {{- /* Changes here will cause the Deployment to restart the pods. */}} {{- include "jupyterhub.matchLabels" . | nindent 8 }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 89110dde41..15a79f6977 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -535,6 +535,7 @@ scheduling: pullPolicy: pullSecrets: [] replicas: 0 + annotations: {} containerSecurityContext: runAsUser: 65534 # nobody user runAsGroup: 65534 # nobody group From cc82d2da171f5adba3d810c2786df85d477d78b3 Mon Sep 17 00:00:00 2001 From: Jon Cotton Date: Fri, 17 Jun 2022 11:58:44 -0600 Subject: [PATCH 370/898] Helm chart feature to annotate the user-scheduler pods --- jupyterhub/schema.yaml | 6 ++++++ .../templates/scheduling/user-scheduler/deployment.yaml | 3 +++ jupyterhub/values.yaml | 1 + 3 files changed, 10 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 2ad3ee25aa..55b4a9a7e6 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2365,6 +2365,12 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + annotations: + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Extra annotations to add to the user-scheduler pods. containerSecurityContext: *containerSecurityContext-spec logLevel: type: integer diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index a5c8695862..832735e4e4 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -16,6 +16,9 @@ spec: {{- include "jupyterhub.matchLabels" . | nindent 8 }} annotations: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} + {{- with .Values.scheduling.userScheduler.annotations }} + {{- . | toYaml | nindent 8 }} + {{- end }} spec: {{ with include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} serviceAccountName: {{ . }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 89110dde41..28d5b899bb 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -506,6 +506,7 @@ scheduling: pullSecrets: [] nodeSelector: {} tolerations: [] + annotations: {} pdb: enabled: true maxUnavailable: 1 From 1c75c5d12cbe0ac21dd0c6d685e63bed8cba106c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 05:02:23 +0000 Subject: [PATCH 371/898] build(deps): bump aquasecurity/trivy-action from 0.3.0 to 0.4.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.3.0 to 0.4.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/4b9b6fb4ef28b31450391a93ade098bb00de584e...49e970d7ac7539dfcc6ddba87b22ddd9b7377540) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index b1bc756c73..61474c02dd 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e + uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e + uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@4b9b6fb4ef28b31450391a93ade098bb00de584e + uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 with: image-ref: rebuilt-image format: table From 79cbb40aed67de39023b43c2b7236fdf45a3eca3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jun 2022 21:03:39 +0000 Subject: [PATCH 372/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v2.6.2 → v2.7.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.6.2...v2.7.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d01523e22d..d8121811ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,7 +53,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.6.2 + rev: v2.7.1 hooks: - id: prettier From 1e91491715e7fb19a7edea841658b7aa275565c1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Jun 2022 12:15:21 +0200 Subject: [PATCH 373/898] hub image: remove aarch64 workaround for pycryptodomex --- images/hub/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 22d0771fd4..d9c637dceb 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -9,8 +9,6 @@ WORKDIR /build-stage # Build wheels for packages that require gcc or other build dependencies and # lack wheels either for amd64 or aarch64. # -# - https://pypi.org/project/pycryptodomex/#files, no aarch64 wheels available -# as of 3.14.1. See https://github.com/Legrandin/pycryptodome/issues/628. # - https://pypi.org/project/pycurl/#files, no wheels available as of 7.45.1. # See https://github.com/pycurl/pycurl/issues/738. # - https://pypi.org/project/ruamel.yaml.clib/#files, no aarch64 wheels @@ -25,7 +23,6 @@ WORKDIR /build-stage COPY requirements.txt requirements.txt RUN pip install build \ && pip wheel \ - $(cat requirements.txt | grep "pycryptodomex==") \ $(cat requirements.txt | grep "pycurl==") \ $(cat requirements.txt | grep "ruamel-yaml-clib==") From 35436cda5283dd61eafe38dee2eec07f23324b1c Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 23 Jun 2022 10:31:56 +0000 Subject: [PATCH 374/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index a422c4abec..b2cf0a6679 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==3.2.2 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.5.18.1 +certifi==2022.6.15 # via # kubernetes-asyncio # requests @@ -90,7 +90,7 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes-asyncio==22.6.5 +kubernetes-asyncio==23.6.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -195,7 +195,7 @@ tornado==6.1 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.2.2.post1 +traitlets==5.3.0 # via # jupyter-telemetry # jupyterhub From a7953cb545b9247fd54215578cf79534e45c6e55 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:48:56 +0000 Subject: [PATCH 375/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b2cf0a6679..4f26994c71 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -179,7 +179,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.37 +sqlalchemy==1.4.39 # via # alembic # jupyterhub From 6088becc8be1f7a5498a79348779503ccd8c2cb8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 24 Jun 2022 19:50:49 +0200 Subject: [PATCH 376/898] Align helm template formatting with helm chart practice --- .../templates/scheduling/user-placeholder/statefulset.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index 739f445b11..268cddc52e 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -23,9 +23,9 @@ spec: serviceName: {{ include "jupyterhub.user-placeholder.fullname" . }} template: metadata: - {{- if .Values.scheduling.userPlaceholder.annotations }} + {{- with .Values.scheduling.userPlaceholder.annotations }} annotations: - {{- toYaml .Values.scheduling.userPlaceholder.annotations | nindent 8 }} + {{- . | toYaml | nindent 8 }} {{- end }} labels: {{- /* Changes here will cause the Deployment to restart the pods. */}} From cdaf1bd904081936f007d8b18a07759c6af784e7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 24 Jun 2022 19:51:58 +0200 Subject: [PATCH 377/898] Align helm template formatting with helm chart practice --- jupyterhub/templates/scheduling/user-scheduler/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 832735e4e4..ab46ae9c68 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -17,7 +17,7 @@ spec: annotations: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} {{- with .Values.scheduling.userScheduler.annotations }} - {{- . | toYaml | nindent 8 }} + {{- . | toYaml | nindent 8 }} {{- end }} spec: {{ with include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} From 9d6f823d2e915e04ac4134f4df164ab81ac1a632 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 8 Jun 2022 10:21:35 +0200 Subject: [PATCH 378/898] docs: relocate changelog to html based docs --- CHANGELOG.md => docs/source/changelog.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CHANGELOG.md => docs/source/changelog.md (100%) diff --git a/CHANGELOG.md b/docs/source/changelog.md similarity index 100% rename from CHANGELOG.md rename to docs/source/changelog.md From e2d1bf3ebf6490525dcf0b9bba2a198a283d2bac Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 8 Jun 2022 10:40:30 +0200 Subject: [PATCH 379/898] docs: add changelog to index --- docs/source/index.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/index.md b/docs/source/index.md index 489cbdb694..f5460ca5de 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -138,3 +138,13 @@ the organizations that support them!) - [Berkeley Institute for Data Science](https://bids.berkeley.edu/) - [Cal Poly, San Luis Obispo](https://www.calpoly.edu/) - [Simula Research Institute](https://www.simula.no/) + +## Changelog + +This section holds describes the changes between versions and how to upgrade +between them. + +```{toctree} +:maxdepth: 1 +changelog +``` From bd7675ea27924e85bb0873decd7de51905407a4e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:02:43 +0200 Subject: [PATCH 380/898] docs: fix small warnings by sphinx in changelog --- docs/source/changelog.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 687ecc8870..ad0c26a5aa 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -38,18 +38,18 @@ If you are not using firstuseauthenticator, you are not affected. ### [1.1.3] - 2021-08-25 -## Maintenance and upkeep improvements +#### Maintenance and upkeep improvements - refactor: remove redundant trimSuffix of new lines after toYaml [#2358](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2358) ([@consideRatio](https://github.com/consideRatio)) - build(deps): bump pycurl from 7.44.0 to 7.44.1 in /images/hub [#2352](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2352) ([@dependabot](https://github.com/dependabot)) - build(deps): bump oauthenticator from 14.1.0 to 14.2.0 in /images/hub [#2350](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2350) ([@dependabot](https://github.com/dependabot)) - build(deps): bump pycurl from 7.43.0.6 to 7.44.0 in /images/hub [#2347](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2347) ([@dependabot](https://github.com/dependabot)) -## Documentation improvements +#### Documentation improvements - Add docs on GitHub team authentication [#2349](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2349) ([@j0nnyr0berts](https://github.com/j0nnyr0berts)) -## Contributors to this release +#### Contributors to this release ([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2021-08-05&to=2021-08-24&type=c)) @@ -691,7 +691,7 @@ This release bumps the JupyterHub version from 1.2.1 to 1.2.2. See [JupyterHub's changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html?highlight=changelog) for more information. -## Bugs fixed +#### Bugs fixed - image: bump JupyterHub to 1.2.2 from 1.2.1 for bugfixes [#1924](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1924) ([@consideRatio](https://github.com/consideRatio)) @@ -738,7 +738,7 @@ rely on Helm 3 features. - Allow exposing extra ports in autohttps/traefik deployment [#1901](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1901) ([@yuvipanda](https://github.com/yuvipanda)) - prePuller.extraTolerations added for the image-puller daemonsets [#1883](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1883) ([@jerkern](https://github.com/jerkern)) -## Bugs fixed +#### Bugs fixed - hub image: kubernetes 12.0.1, nativeauth 0.0.6, tornado 6.1 [#1912](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1912) ([@consideRatio](https://github.com/consideRatio)) @@ -3192,7 +3192,7 @@ In alphabetical order, - [Zhenwen Zhang](https://github.com/zhangzhenwen) - [Zoltan Fedor](https://github.com/zoltan-fedor) -## [0.4] - [Akram](#akram) - 2017-06-23 +## [0.4] - Akram - 2017-06-23 Stability, HTTPS & breaking changes. From 44a0f1777bd853d6acfbfe7b8bbc0baa97fd4c30 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:05:32 +0200 Subject: [PATCH 381/898] docs: avoid linkcheck throttling/failing --- docs/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index bfe40109cf..37433f6ad3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -228,6 +228,9 @@ def parse_schema(d, md=[], depth=0, pre=""): linkcheck_ignore = [ r"(.*)github\.com(.*)#", # javascript based anchors r"(.*)/#%21(.*)/(.*)", # /#!forum/jupyter - encoded anchor edge case + r"https://github.com/[^/]*$", # too many github usernames / searches in changelog + "https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/", # too many PRs in changelog + "https://github.com/jupyterhub/zero-to-jupyterhub-k8s/compare/", # too many comparisons in changelog "https://your-domain.com", # example "https://your-domain-name.com", # example "https://kubernetes.io/docs/tutorials/kubernetes-basics/", # works From 5849b29e31bde54c67390e52b548c3b22c83820e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:17:53 +0200 Subject: [PATCH 382/898] docs: update links to new changelog location --- RELEASE.md | 4 ++-- docs/source/administrator/upgrading.md | 13 +++++++------ docs/source/changelog.md | 2 ++ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index ac9a1d8147..10df04e3bb 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -48,7 +48,7 @@ Also the images we build are based on some image specified in the `FROM` stateme ## Pre-release iteration -- Update `CHANGELOG.md` +- Update `docs/source/changelog.md` - [ ] Generate a list of PRs using [executablebooks/github-activity](https://github.com/executablebooks/github-activity) ```bash @@ -74,7 +74,7 @@ Also the images we build are based on some image specified in the `FROM` stateme ## Final release -- Update `CHANGELOG.md` +- Update `docs/source/changelog.md` - [ ] Generate a list of merged PRs and a list of contributors and update the changelog. ```bash diff --git a/docs/source/administrator/upgrading.md b/docs/source/administrator/upgrading.md index ccd88b2fe7..1aa189fcdb 100644 --- a/docs/source/administrator/upgrading.md +++ b/docs/source/administrator/upgrading.md @@ -1,3 +1,5 @@ +(upgrading)= + # Upgrading your Helm chart This page covers best-practices in upgrading your JupyterHub deployment via updates @@ -5,7 +7,7 @@ to the Helm Chart. Upgrading from one version of the Helm Chart to the next should be as seamless as possible, and generally shouldn't require major -changes to your deployment. Check the [CHANGELOG](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/CHANGELOG.md) +changes to your deployment. Check the [CHANGELOG](changelog) for each release to find out if there are any breaking changes in the newest version. For additional help, feel free to reach out to us on [gitter](https://gitter.im/jupyterhub/jupyterhub) @@ -16,7 +18,7 @@ or the [Discourse forum](https://discourse.jupyter.org/). These steps are **critical** before performing a major upgrade. 1. Always backup your database! -2. Review the [CHANGELOG](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/CHANGELOG.md) for incompatible changes and upgrade instructions. +2. Review the [CHANGELOG](changelog) for incompatible changes and upgrade instructions. 3. Update your configuration accordingly. 4. User servers may need be stopped prior to the upgrade, or restarted after it. @@ -26,7 +28,7 @@ These steps are **critical** before performing a major upgrade. ### v0.5 to v0.6 -See the [CHANGELOG](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/CHANGELOG.md#06---ellyse-perry---2017-01-29). +See the [CHANGELOG](changelog). ### v0.4 to v0.5 @@ -35,9 +37,8 @@ Since it is a major upgrade of JupyterHub that changes how authentication is implemented, user servers must be stopped during the upgrade. The database schema has also changed, so a database upgrade must be performed. -See the [documentation for v0.5 for the upgrade process](https://zero-to-jupyterhub.readthedocs.io/en/latest/administrator/upgrading.html#v0-4-to-v0-5) -as well as the [CHANGELOG](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/CHANGELOG.md#05---hamid-hassan---2017-12-05) -for this release for more information about changes. +See the [CHANGELOG](changelog) for this release for more information about +changes. ## Subtopics diff --git a/docs/source/changelog.md b/docs/source/changelog.md index ad0c26a5aa..ad639a88bb 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -1,3 +1,5 @@ +(changelog)= + # Changelog Here you can find upgrade changes in between releases and upgrade instructions. From 4b866c52cec08e9ed90e8c624684f616ace33148 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:19:03 +0200 Subject: [PATCH 383/898] docs: add CHANGELOG.md placeholder linking to docs --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..00df2789af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1 @@ +The changelog is now available at https://z2jh.jupyter.org/en/latest/changelog.html. From 38284ba3fa1e242ad6d065b722aad2c7b9d94e54 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:38:20 +0200 Subject: [PATCH 384/898] docs: fix heading levels and formatting for a nice TOC --- docs/source/changelog.md | 168 +++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 78 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index ad639a88bb..96da29e3f1 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -29,16 +29,16 @@ Similarly, upgrading the chart without also upgrading JupyterHub to 1.5 in your JupyterHub 1.5 in the user environment is fully compatible with a Hub running 1.4, and _vice versa_. -## [1.1] +## 1.1 -### [1.1.4] - 2021-10-28 +### 1.1.4 - 2021-10-28 Security release! 1.1.4 release fixes a [critical security vulnerability][ghsa-5xvc-vgmp-jgc3] in jupyterhub-firstuse authenticator. If you are not using firstuseauthenticator, you are not affected. [ghsa-5xvc-vgmp-jgc3]: https://github.com/jupyterhub/firstuseauthenticator/security/advisories/GHSA-5xvc-vgmp-jgc3 -### [1.1.3] - 2021-08-25 +### 1.1.3 - 2021-08-25 #### Maintenance and upkeep improvements @@ -57,7 +57,7 @@ If you are not using firstuseauthenticator, you are not affected. [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-08-05..2021-08-24&type=Issues) | [@j0nnyr0berts](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aj0nnyr0berts+updated%3A2021-08-05..2021-08-24&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-08-05..2021-08-24&type=Issues) -### [1.1.2] - 2021-08-05 +### 1.1.2 - 2021-08-05 #### Bugs fixed @@ -73,7 +73,7 @@ If you are not using firstuseauthenticator, you are not affected. [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-07-22..2021-08-05&type=Issues) | [@hiroki-sawano](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ahiroki-sawano+updated%3A2021-07-22..2021-08-05&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-07-22..2021-08-05&type=Issues) | [@MridulS](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AMridulS+updated%3A2021-07-22..2021-08-05&type=Issues) -### [1.1.1] - 2021-07-22 +### 1.1.1 - 2021-07-22 #### Bugs fixed @@ -83,7 +83,7 @@ If you are not using firstuseauthenticator, you are not affected. - ci: misc fixes post 1.1.0 [#2326](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2326) ([@consideRatio](https://github.com/consideRatio)) -### [1.1.0] - 2021-07-21 +### 1.1.0 - 2021-07-21 #### Highlights @@ -101,7 +101,7 @@ If you are not using firstuseauthenticator, you are not affected. The Helm chart is fully arm64 compatible, even the `singleuser.image` that previously wasn't. -### Breaking changes +#### Breaking changes This breaking change only concerns someone that has configured `hub.services..name=` so that `` is different @@ -169,9 +169,9 @@ For a detailed list of how Python dependencies have change in the `hub` Pod's Do [@cdibble](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acdibble+updated%3A2021-06-25..2021-07-21&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-06-25..2021-07-21&type=Issues) | [@jtrouth](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajtrouth+updated%3A2021-06-25..2021-07-21&type=Issues) | [@mallman](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amallman+updated%3A2021-06-25..2021-07-21&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-06-25..2021-07-21&type=Issues) | [@michaellzc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amichaellzc+updated%3A2021-06-25..2021-07-21&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-06-25..2021-07-21&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2021-06-25..2021-07-21&type=Issues) -## [1.0] +## 1.0 -### [1.0.1] - 2021-06-25 +### 1.0.1 - 2021-06-25 #### Bugs fixed @@ -210,7 +210,7 @@ For a detailed list of how Python dependencies have change in the `hub` Pod's Do [@cdibble](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acdibble+updated%3A2021-06-09..2021-06-24&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-06-09..2021-06-24&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2021-06-09..2021-06-24&type=Issues) | [@enolfc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aenolfc+updated%3A2021-06-09..2021-06-24&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-06-09..2021-06-24&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-06-09..2021-06-24&type=Issues) | [@sgibson91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgibson91+updated%3A2021-06-09..2021-06-24&type=Issues) | [@v1r7u](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Av1r7u+updated%3A2021-06-09..2021-06-24&type=Issues) | [@weisdd](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aweisdd+updated%3A2021-06-09..2021-06-24&type=Issues) -### [1.0.0] - 2021-06-09 +### 1.0.0 - 2021-06-09 This release includes a security announcement, breaking changes, several new features, and more. Please read through this to be able to help yourself and @@ -538,9 +538,9 @@ For a detailed list of how Python dependencies have change in the `hub` Pod's Do [@agnewp](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aagnewp+updated%3A2021-01-15..2021-05-28&type=Issues) | [@bbockelm](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abbockelm+updated%3A2021-01-15..2021-05-28&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2021-01-15..2021-05-28&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2021-01-15..2021-05-28&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-01-15..2021-05-28&type=Issues) | [@damianavila](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adamianavila+updated%3A2021-01-15..2021-05-28&type=Issues) | [@danielballan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adanielballan+updated%3A2021-01-15..2021-05-28&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2021-01-15..2021-05-28&type=Issues) | [@dhirschfeld](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adhirschfeld+updated%3A2021-01-15..2021-05-28&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2021-01-15..2021-05-28&type=Issues) | [@jabbera](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajabbera+updated%3A2021-01-15..2021-05-28&type=Issues) | [@jgwerner](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajgwerner+updated%3A2021-01-15..2021-05-28&type=Issues) | [@kafonek](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akafonek+updated%3A2021-01-15..2021-05-28&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-01-15..2021-05-28&type=Issues) | [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ameeseeksmachine+updated%3A2021-01-15..2021-05-28&type=Issues) | [@mhwasil](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amhwasil+updated%3A2021-01-15..2021-05-28&type=Issues) | [@michzimny](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amichzimny+updated%3A2021-01-15..2021-05-28&type=Issues) | [@MickeyShnaiderman-RecoLabs](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AMickeyShnaiderman-RecoLabs+updated%3A2021-01-15..2021-05-28&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-01-15..2021-05-28&type=Issues) | [@mriedem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amriedem+updated%3A2021-01-15..2021-05-28&type=Issues) | [@NerdSec](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ANerdSec+updated%3A2021-01-15..2021-05-28&type=Issues) | [@pcfens](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apcfens+updated%3A2021-01-15..2021-05-28&type=Issues) | [@pvanliefland](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apvanliefland+updated%3A2021-01-15..2021-05-28&type=Issues) | [@remche](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aremche+updated%3A2021-01-15..2021-05-28&type=Issues) | [@roelbaz](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aroelbaz+updated%3A2021-01-15..2021-05-28&type=Issues) | [@rommeld](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arommeld+updated%3A2021-01-15..2021-05-28&type=Issues) | [@RyanQuey](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ARyanQuey+updated%3A2021-01-15..2021-05-28&type=Issues) | [@spenczar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aspenczar+updated%3A2021-01-15..2021-05-28&type=Issues) | [@support](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asupport+updated%3A2021-01-15..2021-05-28&type=Issues) | [@thomasv314](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Athomasv314+updated%3A2021-01-15..2021-05-28&type=Issues) | [@tkislan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atkislan+updated%3A2021-01-15..2021-05-28&type=Issues) | [@willingc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awillingc+updated%3A2021-01-15..2021-05-28&type=Issues) | [@yobome](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayobome+updated%3A2021-01-15..2021-05-28&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2021-01-15..2021-05-28&type=Issues) -## [0.11] +## 0.11 -### [0.11.1] - 2021-01-15 +### 0.11.1 - 2021-01-15 This release fixes a regression in the Ingress resource and a bump of jupyterhub-nativeauthenticator from 0.0.6 to 0.0.7. @@ -553,7 +553,7 @@ jupyterhub-nativeauthenticator from 0.0.6 to 0.0.7. - build(deps): bump jupyterhub-nativeauthenticator from 0.0.6 to 0.0.7 in /images/hub [#1988](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1988) ([@dependabot](https://github.com/dependabot)) -### [0.11.0] - 2021-01-14 +### 0.11.0 - 2021-01-14 Please read the _security announcement_ and the _breaking changes_ below, and also note that this is the last release supporting Helm 2 and k8s versions lower @@ -679,15 +679,15 @@ For a detailed list of how Python dependencies have change in the `hub` Pod's Do [@arokem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarokem+updated%3A2020-11-27..2021-01-13&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2020-11-27..2021-01-13&type=Issues) | [@chicocvenancio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achicocvenancio+updated%3A2020-11-27..2021-01-13&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2020-11-27..2021-01-13&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-11-27..2021-01-13&type=Issues) | [@DArtagan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADArtagan+updated%3A2020-11-27..2021-01-13&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2020-11-27..2021-01-13&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2020-11-27..2021-01-13&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2020-11-27..2021-01-13&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2020-11-27..2021-01-13&type=Issues) | [@naterush](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Anaterush+updated%3A2020-11-27..2021-01-13&type=Issues) | [@rokroskar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arokroskar+updated%3A2020-11-27..2021-01-13&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2020-11-27..2021-01-13&type=Issues) -## [0.10] +## 0.10 -### [0.10.6] - 2020-11-27 +### 0.10.6 - 2020-11-27 This release is a security workaround for jupyterhub/oauthenticator described in https://github.com/jupyterhub/oauthenticator/security/advisories/GHSA-384w-5v3f-q499. Please don't use versions 0.10.0 - 0.10.5 and upgrade to 0.10.6 or later. If any users have been authorized during usage of 0.10.0 - 0.10.5 who should not have been, they must be deleted via the API or admin interface, [per the documentation](https://jupyterhub.readthedocs.io/en/1.2.2/getting-started/authenticators-users-basics.html#add-or-remove-users-from-the-hub). -### [0.10.5] - 2020-11-27 +### 0.10.5 - 2020-11-27 This release bumps the JupyterHub version from 1.2.1 to 1.2.2. See [JupyterHub's changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html?highlight=changelog) @@ -707,7 +707,7 @@ for more information. [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-11-21..2020-11-27&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2020-11-21..2020-11-27&type=Issues) -### [0.10.4] - 2020-11-21 +### 0.10.4 - 2020-11-21 A patch release to patch a bug in the dependency oauthenticator that made users have their servers spawn before they had the chance to choose a server @@ -723,7 +723,7 @@ configuration if c.KubeSpawner.profile_list was configured. [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-11-16..2020-11-21&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2020-11-16..2020-11-21&type=Issues) -### [0.10.3] - 2020-11-16 +### 0.10.3 - 2020-11-16 This release contain minor enhancements and bugfix in a dependency that could have resulted in unwanted hub pod restarts. Helm 2.16+ has been explicitly @@ -766,7 +766,7 @@ rely on Helm 3 features. [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2020-10-30..2020-11-15&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2020-10-30..2020-11-15&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-10-30..2020-11-15&type=Issues) | [@JarnoRFB](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJarnoRFB+updated%3A2020-10-30..2020-11-15&type=Issues) | [@jerkern](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajerkern+updated%3A2020-10-30..2020-11-15&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2020-10-30..2020-11-15&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2020-10-30..2020-11-15&type=Issues) | [@plant99](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aplant99+updated%3A2020-10-30..2020-11-15&type=Issues) | [@tirumerla](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atirumerla+updated%3A2020-10-30..2020-11-15&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2020-10-30..2020-11-15&type=Issues) -### [0.10.2] - 2020-10-30 +### 0.10.2 - 2020-10-30 A bugfix release to add securityContext configuration on _all_ the containers in the image-puller pods, which can be needed when a k8s PodSecurityPolicy is forcing pods to startup as non-root users. @@ -786,7 +786,7 @@ Note that whoever need to comply with a strict PodSecurityPolicy will also need [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-10-30..2020-10-30&type=Issues) | [@jatinder91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajatinder91+updated%3A2020-10-30..2020-10-30&type=Issues) -### [0.10.1] - 2020-10-30 +### 0.10.1 - 2020-10-30 A bugfix release simply updating JupyterHub to 1.2.1. JupyterHub 1.2.1 fixes a regression related to registered JupyterHub services using the `oauth_no_confirm` configuration. @@ -808,7 +808,7 @@ A bugfix release simply updating JupyterHub to 1.2.1. JupyterHub 1.2.1 fixes a r [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-10-29..2020-10-30&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2020-10-29..2020-10-30&type=Issues) -### [0.10.0] - 2020-10-29 +### 0.10.0 - 2020-10-29 This release makes the deployment more robust, and enhances users ability to configure the Helm chart in general. Some defaults have been changed allowing @@ -1077,9 +1077,9 @@ you everyone! [@01100010011001010110010101110000](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3A01100010011001010110010101110000+updated%3A2020-04-15..2020-10-29&type=Issues) | [@ablekh](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aablekh+updated%3A2020-04-15..2020-10-29&type=Issues) | [@aculich](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaculich+updated%3A2020-04-15..2020-10-29&type=Issues) | [@adi413](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aadi413+updated%3A2020-04-15..2020-10-29&type=Issues) | [@agrahamlincoln](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aagrahamlincoln+updated%3A2020-04-15..2020-10-29&type=Issues) | [@aguinaldoabbj](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaguinaldoabbj+updated%3A2020-04-15..2020-10-29&type=Issues) | [@Aisuko](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAisuko+updated%3A2020-04-15..2020-10-29&type=Issues) | [@akaszynski](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aakaszynski+updated%3A2020-04-15..2020-10-29&type=Issues) | [@albertmichaelj](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalbertmichaelj+updated%3A2020-04-15..2020-10-29&type=Issues) | [@alexmorley](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalexmorley+updated%3A2020-04-15..2020-10-29&type=Issues) | [@amanda-tan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aamanda-tan+updated%3A2020-04-15..2020-10-29&type=Issues) | [@arpitsri3](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarpitsri3+updated%3A2020-04-15..2020-10-29&type=Issues) | [@asubb](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aasubb+updated%3A2020-04-15..2020-10-29&type=Issues) | [@aydintd](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaydintd+updated%3A2020-04-15..2020-10-29&type=Issues) | [@bebosudo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abebosudo+updated%3A2020-04-15..2020-10-29&type=Issues) | [@BertR](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ABertR+updated%3A2020-04-15..2020-10-29&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2020-04-15..2020-10-29&type=Issues) | [@betolink](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetolink+updated%3A2020-04-15..2020-10-29&type=Issues) | [@bibz](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abibz+updated%3A2020-04-15..2020-10-29&type=Issues) | [@bleggett](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ableggett+updated%3A2020-04-15..2020-10-29&type=Issues) | [@cam72cam](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acam72cam+updated%3A2020-04-15..2020-10-29&type=Issues) | [@carat64](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acarat64+updated%3A2020-04-15..2020-10-29&type=Issues) | [@cbanek](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbanek+updated%3A2020-04-15..2020-10-29&type=Issues) | [@cboettig](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acboettig+updated%3A2020-04-15..2020-10-29&type=Issues) | [@chancez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achancez+updated%3A2020-04-15..2020-10-29&type=Issues) | [@chicocvenancio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achicocvenancio+updated%3A2020-04-15..2020-10-29&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2020-04-15..2020-10-29&type=Issues) | [@chrisroat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achrisroat+updated%3A2020-04-15..2020-10-29&type=Issues) | [@clkao](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aclkao+updated%3A2020-04-15..2020-10-29&type=Issues) | [@conet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aconet+updated%3A2020-04-15..2020-10-29&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2020-04-15..2020-10-29&type=Issues) | [@craig-willis](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acraig-willis+updated%3A2020-04-15..2020-10-29&type=Issues) | [@cslovell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acslovell+updated%3A2020-04-15..2020-10-29&type=Issues) | [@dalonlobo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adalonlobo+updated%3A2020-04-15..2020-10-29&type=Issues) | [@dalssaso](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adalssaso+updated%3A2020-04-15..2020-10-29&type=Issues) | [@danroliver](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adanroliver+updated%3A2020-04-15..2020-10-29&type=Issues) | [@DarkBlaez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADarkBlaez+updated%3A2020-04-15..2020-10-29&type=Issues) | [@davidsmf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adavidsmf+updated%3A2020-04-15..2020-10-29&type=Issues) | [@deinal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adeinal+updated%3A2020-04-15..2020-10-29&type=Issues) | [@dimm0](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adimm0+updated%3A2020-04-15..2020-10-29&type=Issues) | [@dkipping](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adkipping+updated%3A2020-04-15..2020-10-29&type=Issues) | [@dmpe](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Admpe+updated%3A2020-04-15..2020-10-29&type=Issues) | [@donotpush](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adonotpush+updated%3A2020-04-15..2020-10-29&type=Issues) | [@duongnt](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aduongnt+updated%3A2020-04-15..2020-10-29&type=Issues) | [@easel](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aeasel+updated%3A2020-04-15..2020-10-29&type=Issues) | [@echarles](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aecharles+updated%3A2020-04-15..2020-10-29&type=Issues) | [@Edward-liang](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AEdward-liang+updated%3A2020-04-15..2020-10-29&type=Issues) | [@eric-leblouch](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aeric-leblouch+updated%3A2020-04-15..2020-10-29&type=Issues) | [@erinfry6](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aerinfry6+updated%3A2020-04-15..2020-10-29&type=Issues) | [@etheleon](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aetheleon+updated%3A2020-04-15..2020-10-29&type=Issues) | [@farzadz](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Afarzadz+updated%3A2020-04-15..2020-10-29&type=Issues) | [@filippo82](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Afilippo82+updated%3A2020-04-15..2020-10-29&type=Issues) | [@frankgu968](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Afrankgu968+updated%3A2020-04-15..2020-10-29&type=Issues) | [@frouzbeh](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Afrouzbeh+updated%3A2020-04-15..2020-10-29&type=Issues) | [@GeorgianaElena](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGeorgianaElena+updated%3A2020-04-15..2020-10-29&type=Issues) | [@GergelyKalmar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGergelyKalmar+updated%3A2020-04-15..2020-10-29&type=Issues) | [@gsemet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agsemet+updated%3A2020-04-15..2020-10-29&type=Issues) | [@Guanzhou-Ke](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGuanzhou-Ke+updated%3A2020-04-15..2020-10-29&type=Issues) | [@Gungo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGungo+updated%3A2020-04-15..2020-10-29&type=Issues) | [@h4gen](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ah4gen+updated%3A2020-04-15..2020-10-29&type=Issues) | [@harsimranmaan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aharsimranmaan+updated%3A2020-04-15..2020-10-29&type=Issues) | [@hdimitriou](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ahdimitriou+updated%3A2020-04-15..2020-10-29&type=Issues) | [@hickst](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ahickst+updated%3A2020-04-15..2020-10-29&type=Issues) | [@hnykda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ahnykda+updated%3A2020-04-15..2020-10-29&type=Issues) | [@hqwl159](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ahqwl159+updated%3A2020-04-15..2020-10-29&type=Issues) | [@IamViditAgarwal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIamViditAgarwal+updated%3A2020-04-15..2020-10-29&type=Issues) | [@ilhaan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ailhaan+updated%3A2020-04-15..2020-10-29&type=Issues) | [@ivanpokupec](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aivanpokupec+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jacobtomlinson](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajacobtomlinson+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jahstreet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajahstreet+updated%3A2020-04-15..2020-10-29&type=Issues) | [@JarnoRFB](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJarnoRFB+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jeremievallee](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajeremievallee+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jgerardsimcock](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajgerardsimcock+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jgwerner](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajgwerner+updated%3A2020-04-15..2020-10-29&type=Issues) | [@josibake](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajosibake+updated%3A2020-04-15..2020-10-29&type=Issues) | [@JPMoresmau](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJPMoresmau+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jreadey](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajreadey+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jtlz2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajtlz2+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jtpio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajtpio+updated%3A2020-04-15..2020-10-29&type=Issues) | [@julienchastang](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajulienchastang+updated%3A2020-04-15..2020-10-29&type=Issues) | [@jzf2101](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajzf2101+updated%3A2020-04-15..2020-10-29&type=Issues) | [@kinow](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akinow+updated%3A2020-04-15..2020-10-29&type=Issues) | [@kristofmartens](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akristofmartens+updated%3A2020-04-15..2020-10-29&type=Issues) | [@kyprifog](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akyprifog+updated%3A2020-04-15..2020-10-29&type=Issues) | [@leolb-aphp](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aleolb-aphp+updated%3A2020-04-15..2020-10-29&type=Issues) | [@loki1978](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aloki1978+updated%3A2020-04-15..2020-10-29&type=Issues) | [@ltupin](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Altupin+updated%3A2020-04-15..2020-10-29&type=Issues) | [@lxylxy123456](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Alxylxy123456+updated%3A2020-04-15..2020-10-29&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mathematicalmichael](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amathematicalmichael+updated%3A2020-04-15..2020-10-29&type=Issues) | [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ameeseeksmachine+updated%3A2020-04-15..2020-10-29&type=Issues) | [@meneal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ameneal+updated%3A2020-04-15..2020-10-29&type=Issues) | [@metonymic-smokey](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ametonymic-smokey+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mhwasil](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amhwasil+updated%3A2020-04-15..2020-10-29&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mjuric](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amjuric+updated%3A2020-04-15..2020-10-29&type=Issues) | [@moorepants](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amoorepants+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mpolatcan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ampolatcan+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mriedem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amriedem+updated%3A2020-04-15..2020-10-29&type=Issues) | [@mrocklin](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amrocklin+updated%3A2020-04-15..2020-10-29&type=Issues) | [@NerdSec](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ANerdSec+updated%3A2020-04-15..2020-10-29&type=Issues) | [@nscozzaro](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Anscozzaro+updated%3A2020-04-15..2020-10-29&type=Issues) | [@openthings](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aopenthings+updated%3A2020-04-15..2020-10-29&type=Issues) | [@pcfens](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apcfens+updated%3A2020-04-15..2020-10-29&type=Issues) | [@perllaghu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aperllaghu+updated%3A2020-04-15..2020-10-29&type=Issues) | [@petebachant](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apetebachant+updated%3A2020-04-15..2020-10-29&type=Issues) | [@peterrmah](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apeterrmah+updated%3A2020-04-15..2020-10-29&type=Issues) | [@philvarner](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aphilvarner+updated%3A2020-04-15..2020-10-29&type=Issues) | [@prateekkhera](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aprateekkhera+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rabernat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arabernat+updated%3A2020-04-15..2020-10-29&type=Issues) | [@RAbraham](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ARAbraham+updated%3A2020-04-15..2020-10-29&type=Issues) | [@remche](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aremche+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rkdarst](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arkdarst+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rkevin-arch](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arkevin-arch+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rmoe](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Armoe+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rnestler](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arnestler+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rschroll](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arschroll+updated%3A2020-04-15..2020-10-29&type=Issues) | [@rubdos](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arubdos+updated%3A2020-04-15..2020-10-29&type=Issues) | [@ryanlovett](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aryanlovett+updated%3A2020-04-15..2020-10-29&type=Issues) | [@salvis2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asalvis2+updated%3A2020-04-15..2020-10-29&type=Issues) | [@sampathkethineedi](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asampathkethineedi+updated%3A2020-04-15..2020-10-29&type=Issues) | [@scivm](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ascivm+updated%3A2020-04-15..2020-10-29&type=Issues) | [@Sefriol](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ASefriol+updated%3A2020-04-15..2020-10-29&type=Issues) | [@sgibson91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgibson91+updated%3A2020-04-15..2020-10-29&type=Issues) | [@sgloutnikov](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgloutnikov+updated%3A2020-04-15..2020-10-29&type=Issues) | [@shenghu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ashenghu+updated%3A2020-04-15..2020-10-29&type=Issues) | [@snickell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asnickell+updated%3A2020-04-15..2020-10-29&type=Issues) | [@sstarcher](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asstarcher+updated%3A2020-04-15..2020-10-29&type=Issues) | [@stefansedich](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Astefansedich+updated%3A2020-04-15..2020-10-29&type=Issues) | [@stevenstetzler](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Astevenstetzler+updated%3A2020-04-15..2020-10-29&type=Issues) | [@stv0g](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Astv0g+updated%3A2020-04-15..2020-10-29&type=Issues) | [@subwaymatch](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asubwaymatch+updated%3A2020-04-15..2020-10-29&type=Issues) | [@summerswallow-whi](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asummerswallow-whi+updated%3A2020-04-15..2020-10-29&type=Issues) | [@superyaniv](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asuperyaniv+updated%3A2020-04-15..2020-10-29&type=Issues) | [@support](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asupport+updated%3A2020-04-15..2020-10-29&type=Issues) | [@suryag10](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asuryag10+updated%3A2020-04-15..2020-10-29&type=Issues) | [@TiemenSch](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ATiemenSch+updated%3A2020-04-15..2020-10-29&type=Issues) | [@tirumerla](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atirumerla+updated%3A2020-04-15..2020-10-29&type=Issues) | [@tjcrone](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atjcrone+updated%3A2020-04-15..2020-10-29&type=Issues) | [@tmshn](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atmshn+updated%3A2020-04-15..2020-10-29&type=Issues) | [@TomasBeuzen](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ATomasBeuzen+updated%3A2020-04-15..2020-10-29&type=Issues) | [@tracek](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atracek+updated%3A2020-04-15..2020-10-29&type=Issues) | [@verdurin](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Averdurin+updated%3A2020-04-15..2020-10-29&type=Issues) | [@vindvaki](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Avindvaki+updated%3A2020-04-15..2020-10-29&type=Issues) | [@vishwesh5](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Avishwesh5+updated%3A2020-04-15..2020-10-29&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awelcome+updated%3A2020-04-15..2020-10-29&type=Issues) | [@willingc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awillingc+updated%3A2020-04-15..2020-10-29&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2020-04-15..2020-10-29&type=Issues) | [@zxcGrace](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AzxcGrace+updated%3A2020-04-15..2020-10-29&type=Issues) -## [0.9] +## 0.9 -### [0.9.0] - 2020-04-15 +### 0.9.0 - 2020-04-15 #### Release summary @@ -1195,7 +1195,7 @@ the following upgrade attempt. - Changelog for 0.9.0-beta.4 [#1585](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1585) ([@manics](https://github.com/manics)) - freeze environment in hub image [#1562](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1562) ([@minrk](https://github.com/minrk)) -### [0.9.0-beta.4] - 2020-02-26 +### 0.9.0-beta.4 - 2020-02-26 #### Added @@ -1223,7 +1223,7 @@ the following upgrade attempt. - Added documentation of secret https mode [#1553](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1553) ([@RossRKK](https://github.com/RossRKK)) - Helm 3 preview [#1543](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1543) ([@manics](https://github.com/manics)) -### [0.9.0-beta.3] - 2020-01-17 +### 0.9.0-beta.3 - 2020-01-17 #### Dependency updates @@ -1240,13 +1240,13 @@ the following upgrade attempt. - Fix duplicate docs label [#1544](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1544) ([@manics](https://github.com/manics)) - Made GCP docs of compute zone names generic [#1431](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1431) ([@metonymic-smokey](https://github.com/metonymic-smokey)) -### [0.9.0-beta.2] - 2019-12-26 +### 0.9.0-beta.2 - 2019-12-26 #### Fixed - Fix major breaking change if all HTTPS options was disabled introduced just before beta.1 [#1534](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1534) ([@dirkcgrunwald](https://github.com/dirkcgrunwald)) -### [0.9.0-beta.1] - 2019-12-26 +### 0.9.0-beta.1 - 2019-12-26 Some highlights of relevance for this release are: @@ -1373,17 +1373,17 @@ Some highlights of relevance for this release are: - Update user-environment.rst [#1217](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1217) ([@manycoding](https://github.com/manycoding)) - Add Digital Ocean Cloud Instructions for Kubernetes [#1192](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/1192) ([@alexmorley](https://github.com/alexmorley)) -## [0.8] +## 0.8 -### [0.8.2] - 2019-04-01 +### 0.8.2 - 2019-04-01 Bumped the underlying JupyterHub to 0.9.6. -### [0.8.1] - 2019-03-28 +### 0.8.1 - 2019-03-28 Bumped the underlying JupyterHub to 0.9.5. -### [0.8.0] - [Richie Benaud](https://en.wikipedia.org/wiki/Richie_Benaud) - 2019-01-24 +### 0.8.0 - [Richie Benaud](https://en.wikipedia.org/wiki/Richie_Benaud) - 2019-01-24 This release contains JupyterHub version 0.9.4. It requires Kubernetes >= 1.11 and Helm >= 2.11.0. See [the Helm Chart repository](https://github.com/jupyterhub/helm-chart#release-notes) for @@ -1484,7 +1484,7 @@ Want to scale up before users arrive so they don't end up waiting for the node t - **Updates to the guide** - #850 - **Updates to inline documentation** - #939 -#### [Richie Benaud](https://www.cricket.com.au/players/richie-benaud/gvp5xSjUp0q6Qd7IM5TbCg) +#### Richie Benaud(https://www.cricket.com.au/players/richie-benaud/gvp5xSjUp0q6Qd7IM5TbCg) _(excerpt from https://www.cricket.com.au/players/richie-benaud/gvp5xSjUp0q6Qd7IM5TbCg)_ @@ -1925,7 +1925,9 @@ on issues, PRs and reviews since the last Zero to JupyterHub release. [邱雨波](https://github.com/CraftHeart) [高彦涛](https://github.com/gytlinux) -## [0.7.0](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/compare/v0.6...0.7.0) - [Alex Blackwell](https://en.wikipedia.org/wiki/Alex_Blackwell) - 2018-09-03 +## 0.7 + +### 0.7.0 - [Alex Blackwell](https://en.wikipedia.org/wiki/Alex_Blackwell) - 2018-09-03 This release contains JupyterHub version 0.9.2, additional configuration options and various bug fixes. @@ -1934,7 +1936,7 @@ and various bug fixes. point and have their pod restarted. You may want to give them a heads up ahead of time or do it during nighttime if none are active then. -### Upgrading from v0.6 +#### Upgrading from v0.6 If you are running `v0.5` of the chart, you should upgrade to `v0.6` first before upgrading to `0.7.0`. You can find out what version you are using by @@ -1942,7 +1944,7 @@ running `helm list`. Follow the steps below to upgrade from `v0.6` to `0.7.0`. -#### 1. (Optional) Ensure the hub's and users' data isn't lost +##### 1. (Optional) Ensure the hub's and users' data isn't lost This step is optional, but a recommended safeguard when the hub's and users' data is considered important. The changes makes the PersistentVolumes (PVs), @@ -1964,7 +1966,7 @@ do done ``` -#### 2. Update Helm (v2.9.1+ required) +##### 2. Update Helm (v2.9.1+ required) ```sh # Update helm @@ -1982,7 +1984,7 @@ helm version # Server: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"} ``` -#### 3. (Optional) Clean up pre-puller resources +##### 3. (Optional) Clean up pre-puller resources The pre-puller component of v0.6 could leave leftover resources after it finished, instead of cleaning up after itself. @@ -2002,7 +2004,7 @@ done kubectl delete $resource_types --selector hub.jupyter.org/deletable=true --namespace $NAMESPACE --now ``` -#### 4. (Recommended) Clean up problematic revisions in your Helm release +##### 4. (Recommended) Clean up problematic revisions in your Helm release This step is recommended due to bugs in Helm that could cause your JupyterHub Helm chart installation (release) to get stuck in an invalid state. @@ -2036,7 +2038,7 @@ done kubectl delete configmap --selector "NAME=$RELEASE_NAME,STATUS in (FAILED,PENDING_UPGRADE)" --namespace kube-system ``` -#### 5. Perform the upgrade +##### 5. Perform the upgrade **IMPORTANT:** Do not miss out on the `--force` flag! `--force` is required due to changes in labelling of jupyterhub resources @@ -2060,7 +2062,7 @@ helm upgrade $RELEASE_NAME jupyterhub/jupyterhub --install \ --timeout 1800 ``` -#### 6. Manage active users +##### 6. Manage active users Active users with running pods must restart their pods. If they don't the next time they attempt to access their server they may end up with `{“error”: “invalid_redirect_uri”, “error_description”: “Invalid redirect URI”}`. @@ -2081,7 +2083,7 @@ kubectl delete pod --selector component=singleuser-server --namespace $NAMESPACE kubectl delete pod --selector component=hub --namespace $NAMESPACE ``` -#### Troubleshooting - Cleanup of cluster +##### Troubleshooting - Cleanup of cluster If things fail, you can try the following before installing the chart. If you decide to take these steps, we recommend step 1 is taken first in order to not @@ -2113,7 +2115,7 @@ If you took these steps and step 1, you should probably right now continue with the next troubleshooting section about making `Released` PVs `Available` for reuse. -#### Troubleshooting - Make `Released` PVs `Available` for reuse +##### Troubleshooting - Make `Released` PVs `Available` for reuse If you followed step 1 and 2, you can after cleanup of a cluster reuse the old hub's and users' storage if you do this step before you installs the Helm chart @@ -2142,7 +2144,7 @@ do done ``` -### Contributors +#### Contributors [A. Tan ](https://github.com/amanda-tan) [Aaron Culich](https://github.com/aculich) @@ -2755,16 +2757,18 @@ done [武晨光](https://github.com/mission-young) [陈镇秋](https://github.com/ChenZhenQiu) -## [0.6] - [Ellyse Perry](https://en.wikipedia.org/wiki/Ellyse_Perry) - 2017-01-29 +## 0.6 + +### 0.6 - [Ellyse Perry](https://en.wikipedia.org/wiki/Ellyse_Perry) - 2017-01-29 This release is primarily focused on better support for Autoscaling, Microsoft Azure support & better default security. There are also a number of bug fixes and configurability improvements! -### Breaking changes +#### Breaking changes -#### Pre-puller configuration +##### Pre-puller configuration In prior versions (v0.5), if you wanted to disable the pre-puller, you would use: @@ -2784,7 +2788,7 @@ prePuller: See the [pre-puller docs](http://zero-to-jupyterhub.readthedocs.io/en/latest/advanced.html#pre-pulling-images-for-faster-startup) for more info! -### Upgrading from 0.5 +#### Upgrading from 0.5 This release does not require any special steps to upgrade from v0.5. See the [upgrade documentation](https://zero-to-jupyterhub.readthedocs.io/en/latest/upgrading.html) for general upgrading steps. @@ -2793,7 +2797,7 @@ If you are running v0.4 of the chart, you should upgrade to v0.5 first before upgrading to v0.6. You can find out what version you are using by running `helm list`. -#### Troubleshooting +##### Troubleshooting If your helm upgrade fails due to the error `no Ingress with the name "jupyterhub-internal" found`, you may be experiencing a [helm bug](https://github.com/kubernetes/helm/issues/3275). To work @@ -2801,9 +2805,9 @@ around this, run `kubectl --namespace= delete ingress jupyterhub re-run the `helm upgrade` command. Note that this will cause a short unavailability of your hub over HTTPS, which will resume normal availability once the deployment upgrade completes. -### New Features +#### New Features -#### More secure by default +##### More secure by default z2jh is more secure by default with 0.6. We now block access to cloud security metadata endpoints by @@ -2812,7 +2816,7 @@ default. See the [security documentation](http://zero-to-jupyterhub.readthedocs.io/en/latest/security.html) for more details. It has seen a number of improvements, and we recommend you read through it! -#### Autoscaling improvements +##### Autoscaling improvements Some cloud providers support the [kubernetes node autoscaler](https://github.com/kubernetes/autoscaler/tree/HEAD/cluster-autoscaler), which can add / remove nodes depending on how much your @@ -2833,7 +2837,7 @@ changes to let z2jh interact better with the autoscaler! There is more work to be done for good autoscaling support, but this is a good start! -#### Better Azure support +##### Better Azure support Azure's new managed Kubernetes service ([AKS](https://docs.microsoft.com/en-us/azure/aks/)) is much better supported by this version! @@ -2846,7 +2850,7 @@ before using it in any production workloads! See the [setting up Kubernetes on Microsoft AKS](http://zero-to-jupyterhub.readthedocs.io/en/latest/create-k8s-cluster.html#setting-up-kubernetes-on-microsoft-azure-container-service-aks) section for more information. -#### Better configurability +##### Better configurability We now have better documentation and bug fixes for configurability! @@ -2861,7 +2865,7 @@ We now have better documentation and bug fixes for configurability! - [Better instructions](http://zero-to-jupyterhub.readthedocs.io/en/latest/user-environment.html#pre-populating-user-s-home-directory-with-files) on pre-populating your user's filesystem using [nbgitpuller](https://github.com/data-8/nbgitpuller) -### [Ellyse Perry](https://en.wikipedia.org/wiki/Ellyse_Perry) +#### [Ellyse Perry](https://en.wikipedia.org/wiki/Ellyse_Perry) _(excerpt from https://www.cricket.com.au/players/ellyse-perry/1aMxKNyEOUiJqhq7N5Tlwg)_ @@ -2894,7 +2898,7 @@ selected for the Team of the Tournament in 2012 and 2014. She was named [ICC Female Cricketer of the Year](http://www.abc.net.au/news/2017-12-22/ellyse-perry-named-iccs-womens-cricketer-of-the-year/9280538) in 2017. -### Contributors +#### Contributors This release wouldn't have been possible without the wonderful contributors to the [zero-to-jupyterhub](https://github.com/jupyterhub/zero-to-jupyterhub-k8s), @@ -2930,17 +2934,19 @@ In alphabetical order, - [Yuvi Panda](https://github.com/yuvipanda) - [ZachGlassman](https://github.com/ZachGlassman) -## [0.5] - [Hamid Hassan](http://www.espncricinfo.com/afghanistan/content/player/311427.html) - 2017-12-05 +## 0.5 + +### 0.5 - [Hamid Hassan](http://www.espncricinfo.com/afghanistan/content/player/311427.html) - 2017-12-05 JupyterHub 0.8, HTTPS & scalability. -### Upgrading from 0.4 +#### Upgrading from 0.4 See the [upgrade documentation](https://zero-to-jupyterhub.readthedocs.io/en/latest/upgrading.html) for upgrade steps. -### New Features +#### New Features -#### JupyterHub 0.8 +##### JupyterHub 0.8 JupyterHub 0.8 is full of new features - see [CHANGELOG](https://jupyterhub.readthedocs.io/en/0.8.1/changelog.html#id1) for more details. Specific features made to benefit this chart are: @@ -2959,7 +2965,7 @@ for more details. Specific features made to benefit this chart are: And lots more! -#### Much easier HTTPS +##### Much easier HTTPS It is our responsibility as software authors to make it very easy for admins to set up HTTPS for their users. v0.5 makes this much easier than v0.4. You can find the new @@ -2968,7 +2974,7 @@ they are much simpler! You can also now use your own HTTPS certificates & keys rather than using Let's Encrypt. -#### More authenticators supported +##### More authenticators supported The following new authentication providers have been added: @@ -2978,7 +2984,7 @@ The following new authentication providers have been added: You can also set up a whitelist of users by adding to the list in `auth.whitelist.users`. -#### Easier customization of `jupyterhub_config.py` +##### Easier customization of `jupyterhub_config.py` You can always put extra snippets of `jupyterhub_config.py` configuration in `hub.extraConfig`. Now you can also add extra environment variables to the hub @@ -2987,13 +2993,13 @@ items can be arbitrary YAML, and you can read them via the `get_config` function your `hub.extraConfig`. This makes it cleaner to customize the hub's config in ways that's not yet possible with config.yaml. -#### Hub Services support +##### Hub Services support You can also add [external JupyterHub Services](http://jupyterhub.readthedocs.io/en/latest/reference/services.html) by adding them to `hub.services`. Note that you are still responsible for actually running the service somewhere (perhaps as a deployment object). -#### More customization options for user server environments +##### More customization options for user server environments More options have been added under `singleuser` to help you customize the environment that the user is spawned in. You can change the uid / gid of the user with `singleuser.uid` @@ -3001,7 +3007,7 @@ and `singleuser.fsGid`, mount extra volumes with `singleuser.storage.extraVolume `singleuser.storage.extraVolumeMounts` and provide extra environment variables with `singleuser.extraEnv`. -### Hamid Hassan +#### Hamid Hassan Hamid Hassan is a fast bowler who currently plays for the Afghanistan National Cricket Team. With nicknames ranging from @@ -3014,7 +3020,7 @@ he plays because "We are ambassadors for our country and we want to show the world that Afghanistan is not like people recognise it by terrorists and these things. We want them to know that we have a lot of talent as well" -### Contributors +#### Contributors This release wouldn't have been possible without the wonderful contributors to the [zero-to-jupyterhub](https://github.com/jupyterhub/zero-to-jupyterhub-k8s), @@ -3194,18 +3200,20 @@ In alphabetical order, - [Zhenwen Zhang](https://github.com/zhangzhenwen) - [Zoltan Fedor](https://github.com/zoltan-fedor) -## [0.4] - Akram - 2017-06-23 +## 0.4 + +### 0.4 - Akram - 2017-06-23 Stability, HTTPS & breaking changes. -### Installation and upgrades +#### Installation and upgrades We **recommend** that you delete prior versions of the package and install the latest version. If you are very familiar with Kubernetes, you can upgrade from an older version, but we still suggest deleting and recreating your installation. -### Breaking changes +#### Breaking changes - The **name of a user pod** and a **dynamically created home directory [PVC (PersistentVolumeClaim)](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims)** no longer include the `userid` in them by default. If you are using dynamic PVCs for `home` @@ -3238,7 +3246,7 @@ installation. secretToken: ``` -### Added +#### Added - Added **GitHub Authentication support**, thanks to [Jason Kuruzovich](https://github.com/jkuruzovich). - Added **[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) support**! @@ -3250,7 +3258,7 @@ installation. - **PostgreSQL** is now a supported hub database backend provider. - You can set annotations & labels on the **proxy-public service** now. -### Changed +#### Changed - We now use the official [configurable http proxy](http://github.com/jupyterhub/configurable-http-proxy) (CHP) as the proxy, rather than the unofficial @@ -3261,13 +3269,13 @@ installation. [python client](https://github.com/kubernetes-incubator/client-python/) rather than [pycurl](http://pycurl.io/). This helps with scalability a little. -### Removed +#### Removed - The deprecated `createNamespace` parameter no longer works, alongside the deprecated `name` parameter. You probably weren't using these anyway - they were kept only for backwards compatibility with very early versions. -### Contributors +#### Contributors This release made possible by the awesome work of the following contributors (in alphabetical order): @@ -3280,7 +3288,7 @@ This release made possible by the awesome work of the following contributors <3 -### Akram +#### Akram [Wasim Akram](https://en.wikipedia.org/wiki/Wasim_Akram) (وسیم اکرم) is considered by many to be the greatest pace bowler of all time and a founder of the fine art of @@ -3288,18 +3296,22 @@ the greatest pace bowler of all time and a founder of the fine art of ## 0.3 -### [0.3.1] - 2017-05-19 +### 0.3.1 - 2017-05-19 KubeSpawner updates. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.3.1) -### [0.3] - 2017-05-15 +### 0.3 - 2017-05-15 Deployer UX fixes. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.3) -## [0.2] - 2017-05-01 +## 0.2 + +### 0.2 - 2017-05-01 Minor cleanups and features. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.2) -## [0.1] - 2017-04-10 +## 0.1 + +### 0.1 - 2017-04-10 Initial Public Release. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.1) From 056ba151e79395e283ab274850d77e6cdcca2afb Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 11:48:30 +0200 Subject: [PATCH 385/898] docs: clarify the changelogs unreleased section --- docs/source/changelog.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 96da29e3f1..6adc20f703 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -4,7 +4,13 @@ Here you can find upgrade changes in between releases and upgrade instructions. -## UNRELEASED +## Unreleased breaking changes + +This Helm chart provides [development +releases](https://jupyterhub.github.io/helm-chart/#development-releases-jupyterhub), +and as we merge [breaking changes in pull +requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), +this list should be updated. ## 1.2 From b2cdb75d4aab4279fad04fa9e0f9835aad73a0c4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 26 Jun 2022 13:18:01 +0200 Subject: [PATCH 386/898] docs: transition glossyar to myst --- docs/source/resources/glossary.md | 228 ++++++++++++++---------------- 1 file changed, 110 insertions(+), 118 deletions(-) diff --git a/docs/source/resources/glossary.md b/docs/source/resources/glossary.md index b39c79640d..37f7e5e846 100644 --- a/docs/source/resources/glossary.md +++ b/docs/source/resources/glossary.md @@ -7,124 +7,116 @@ of the components in JupyterHub, see {ref}`tools`. Here we try to keep the definition as succinct and relevant as possible, and provide links to learn more details. - -```{eval-rst} -.. glossary:: - - `admin user `_ - A user who can access the JupyterHub admin panel. They can start/stop user - pods, and potentially access their notebooks. - - `authenticator `_ - The way in which users are authenticated to log into JupyterHub. There - are many authenticators available, like GitHub, Google, MediaWiki, - Dummy (anyone can log in), etc. - - `config.yaml` - The :term:`Helm charts ` templates are rendered with these - :term:`Helm values` as input. The file is written in the `YAML - `_ format. The YAML format is essential - to grasp if working with Kubernetes and Helm. - - container - A container is a isolated working space which for us gives users the - tools, libraries, and capabilities to be productive. - - culler - A separate process in the JupyterHub that stops the user pods of users who - have not been active in a configured interval. - - Dockerfile - A Dockerfile declares how to build a :term:`Docker image`. - - Docker image - A Docker image, built from a :term:`Dockerfile`, allows tools like - ``docker`` to create any number of :term:`containers `. - - image registry - A service for storing Docker images so that they can be stored - and used later. - The default public registry is at https://hub.docker.com, - but you can also run your own private image registry. - Many cloud providers offer private image registry services. - - `environment variables `_ - A set of named values that can affect the way running processes will - behave on a computer. Some common examples are ``PATH``, ``HOME``, and - ``EDITOR``. - - `Helm chart `_ - A Helm chart is a group of :term:`Helm templates ` that - can, given its default values and overrides in provided ``yaml`` files, - render to a set of :term:`Kubernetes resources ` that - can be easily installed to your Kubernetes cluster. In other words a Helm - chart is like a configurable installation of software and infrastructure - to exist on a cloud. - - `Helm template `_ - A Helm template (``.yaml`` files), can given values, render to a - :term:`Kubernetes resource`. - - `Helm values `_ - :term:`Helm charts ` has a set of predefined values - (`values.yaml`) typically overridden by other values in `config.yaml`. The - final values are used to generate :term:`Kubernetes resources ` from :term:`Helm templates ` within a - :term:`Helm chart`. - - Kubernetes - For our purposes, you can think of Kubernetes as a way to speak to a cloud - and describe what you would like it to do, in a manner that isn't specific - for that cloud. - - - `The Illustrated Children's Guide to Kubernetes `_ - - `The official "What is Kubernetes?" text `_ - - Kubernetes API server - The `Kubernetes API - `_ server, - also referred to as the master, will answer questions and update the - desired state of the cluster for you. When you use ``kubectl`` you - communicate with the API server. - - Kubernetes Pod - *Pods* are the smallest deployable units of computing that can be created - and managed in Kubernetes. A pod will use a :term:`Docker image` to create - a container, and most often a controller such as a Deployment will ensure - there is always X running pods of a kind. - - See the `Kubernetes documentation - `__ for more - information. - - Kubernetes resource - A Kubernetes resource can for example be a `Deployment - `_, - `Service - `_ or a - `Secret `_. It - is something you can request by the :term:`Kubernetes API server` to be - present in the cluster. - - persistent storage - A filesystem attached to a user pod that allows the user to store - notebooks and files that persist across multiple logins. - - Node Pool - A *node pool* or *node group* represents a set of nodes of the same kind. - With cluster autoscaling, a node pool can grow and shrink based on demand - allowing you to save computational resources. - - `repo2docker `_ - A tool which lets you quickly convert a Git repository into a - :term:`Docker image`. - - `spawner `_ - A spawner is a separate process created for each active user by - JupyterHub. They are each responsible for one user. This Helm chart relies - on `KubeSpawner - `_. +```{glossary} +[admin user](https://jupyterhub.readthedocs.io/en/stable/getting-started/authenticators-users-basics.html?highlight=admin) + A user who can access the JupyterHub admin panel. They can start/stop user + pods, and potentially access their notebooks. + +[authenticator](https://jupyterhub.readthedocs.io/en/latest/reference/authenticators.html) + The way in which users are authenticated to log into JupyterHub. There + are many authenticators available, like GitHub, Google, MediaWiki, + Dummy (anyone can log in), etc. + +`config.yaml` + The {term}`Helm charts ` templates are rendered with these + {term}`Helm values` as input. The file is written in the [YAML](https://en.wikipedia.org/wiki/YAML) format. The YAML format is essential + to grasp if working with Kubernetes and Helm. + +container + A container is a isolated working space which for us gives users the + tools, libraries, and capabilities to be productive. + +culler + A separate process in the JupyterHub that stops the user pods of users who + have not been active in a configured interval. + +Dockerfile + A Dockerfile declares how to build a {term}`Docker image`. + +Docker image + A Docker image, built from a {term}`Dockerfile`, allows tools like + `docker` to create any number of {term}`containers `. + +image registry + A service for storing Docker images so that they can be stored + and used later. + The default public registry is at https://hub.docker.com, + but you can also run your own private image registry. + Many cloud providers offer private image registry services. + +[environment variables](https://en.wikipedia.org/wiki/Environment_variable) + A set of named values that can affect the way running processes will + behave on a computer. Some common examples are `PATH`, `HOME`, and + `EDITOR`. + +[Helm chart](https://helm.sh/docs/topics/charts/) + A Helm chart is a group of {term}`Helm templates ` that + can, given its default values and overrides in provided `yaml` files, + render to a set of {term}`Kubernetes resources ` that + can be easily installed to your Kubernetes cluster. In other words a Helm + chart is like a configurable installation of software and infrastructure + to exist on a cloud. + +[Helm template](https://helm.sh/docs/chart_template_guide/) + A Helm template (`.yaml` files), can given values, render to a + {term}`Kubernetes resource`. + +[Helm values](https://helm.sh/docs/chart_template_guide/values_files/) + {term}`Helm charts ` has a set of predefined values + (`values.yaml`) typically overridden by other values in `config.yaml`. The + final values are used to generate {term}`Kubernetes resources ` from {term}`Helm templates ` within a + {term}`Helm chart`. + +Kubernetes + For our purposes, you can think of Kubernetes as a way to speak to a cloud + and describe what you would like it to do, in a manner that isn't specific + for that cloud. + + - [The Illustrated Children's Guide to Kubernetes](https://www.youtube.com/watch?v=4ht22ReBjno) + - [The official "What is Kubernetes?" text](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/) + +Kubernetes API server + The [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/) server, + also referred to as the master, will answer questions and update the + desired state of the cluster for you. When you use `kubectl` you + communicate with the API server. + +Kubernetes Pod + *Pods* are the smallest deployable units of computing that can be created + and managed in Kubernetes. A pod will use a {term}`Docker image` to create + a container, and most often a controller such as a Deployment will ensure + there is always X running pods of a kind. + + See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/workloads/pods/) for more + information. + +Kubernetes resource + A Kubernetes resource can for example be a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/), + [Service](https://kubernetes.io/docs/concepts/services-networking/service/) or a + [Secret](https://kubernetes.io/docs/concepts/configuration/secret/). It + is something you can request by the {term}`Kubernetes API server` to be + present in the cluster. + +persistent storage + A filesystem attached to a user pod that allows the user to store + notebooks and files that persist across multiple logins. + +Node Pool + A *node pool* or *node group* represents a set of nodes of the same kind. + With cluster autoscaling, a node pool can grow and shrink based on demand + allowing you to save computational resources. + +[repo2docker](https://github.com/jupyterhub/repo2docker) + A tool which lets you quickly convert a Git repository into a + {term}`Docker image`. + +[spawner](https://jupyterhub.readthedocs.io/en/stable/getting-started/spawners-basics.html) + A spawner is a separate process created for each active user by + JupyterHub. They are each responsible for one user. This Helm chart relies + on [KubeSpawner](https://jupyterhub-kubespawner.readthedocs.io/en/latest/). ``` From 24151008e2e8fcf7544654d136dcc330c3266d51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jun 2022 05:03:17 +0000 Subject: [PATCH 387/898] build(deps): bump aquasecurity/trivy-action from 0.4.1 to 0.5.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.4.1 to 0.5.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/49e970d7ac7539dfcc6ddba87b22ddd9b7377540...7b7aa264d83dc58691451798b4d117d53d21edfe) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 61474c02dd..23b68d155c 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 + uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 + uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@49e970d7ac7539dfcc6ddba87b22ddd9b7377540 + uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe with: image-ref: rebuilt-image format: table From f843296b13077a0c2642ad6fac9f9f2665042c5a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Jun 2022 05:29:28 +0000 Subject: [PATCH 388/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 3edb5ce1a0..525f5683f8 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-06-13_13:05:46 +# VULN_SCAN_TIME=2022-06-27_05:29:26 USER root RUN apt-get update \ From fb92e67744326f87f2e1c92226b743c31ea2eb90 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Jun 2022 06:14:12 +0000 Subject: [PATCH 389/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index d9c637dceb..53ac003863 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -2,7 +2,7 @@ # --------------- FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-05-30_05:27:26 +# VULN_SCAN_TIME=2022-06-27_06:14:10 WORKDIR /build-stage From ce305ca716a5c8c8ae10033c44e752642e9027e9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Jun 2022 09:31:00 +0200 Subject: [PATCH 390/898] docs: update changelog links --- docs/source/changelog.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6adc20f703..a2359f2ce7 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -99,7 +99,7 @@ If you are not using firstuseauthenticator, you are not affected. `hub.services` with an api token. This is especially helpful for Helm charts depending on this Helm chart such as `binderhub` or `daskhub`, for more details see the - [`hub.services`](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#hub-services) + [`hub.services`](schema_hub.services) entry in the configuration reference. - **Full arm64 compatebility** @@ -241,7 +241,7 @@ and small bugfixes will increment the three version numbers. dedicated ConfigMap that was mounted etc before, you don't need to go through that trouble. - Read more in [the configuration reference](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#hub-extrafiles). + Read more in [the configuration reference](schema_hub.extraFiles). - **Automatic secret generation** @@ -267,7 +267,7 @@ and small bugfixes will increment the three version numbers. Helm chart, but should _not be used_ unless you install from scratch. Read more in [the configuration - reference](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#fullnameOverride). + reference](schema_fullnameOverride). - **Referencing resources from a parent Helm chart's templates** @@ -394,7 +394,7 @@ followed these instructions between `0.7.0-beta.1` and `0.11.1`, please see the - **hub.existingSecret is reworked** ([#2042](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2042)) See [the - documentation](http://z2jh.jupyter.org/en/latest/resources/reference.html#hub-existingsecret) + documentation](schema_hub.existingSecret) and [pull request #2042](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2042) for more details. @@ -840,7 +840,7 @@ the Helm chart to easier comply with PodSecurityPolicies by default. instead use the chart wide `imagePullSecret` with the same syntax which will be helping all the JupyterHub pod's get images from a private image registry. For more information, see [the configuration - reference](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#imagepullsecret). + reference](schema_imagePullSecret). - Predefined Kubernetes [NetworkPolicies](https://kubernetes.io/docs/concepts/services-networking/network-policies/) @@ -860,7 +860,7 @@ the Helm chart to easier comply with PodSecurityPolicies by default. See the [security documentation](https://zero-to-jupyterhub.readthedocs.io/en/latest/administrator/security.html#kubernetes-network-policies) and the [configuration - reference](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#proxy-chp-networkpolicy) + reference](schema_proxy.chp.networkPolicy) for more details. - The Helm chart configuration `proxy.networkPolicy` has been removed, @@ -879,13 +879,13 @@ the Helm chart to easier comply with PodSecurityPolicies by default. - **Environment variables in pods with K8S config**. An ability to configure environment variables in pods with a k8s native syntax has been added. This allows you to reference and mount a field in a k8s Secret as an environment variable for example. For more information, read [about - extraEnv](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#singleuser-extraenv) + extraEnv](schema_singleuser.extraEnv) in the configuration reference. - **Configure secrets for all pods via the helm chart**. imagePullSecrets for all the pods in the Helm chart can now be configured chart wide. See the configuration reference about - [imagePullSecret](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#imagepullsecret) + [imagePullSecret](schema_imagePullSecret) and - [imagePullSecrets](https://zero-to-jupyterhub.readthedocs.io/en/latest/resources/reference.html#imagepullsecrets) + [imagePullSecrets](schema_imagePullSecrets) for more details. - **Pod security is easier to use and configure**. Deploying the Helm chart in a cluster with a PodSecurityPolicy active is now easier, because the pods' containers now have `securityContext` set on them to From 1dd026a3d7d22ac172ccbe48d39733570cda62c1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Jun 2022 10:05:14 +0200 Subject: [PATCH 391/898] docs: remove broken links and use https over http in a few --- .github/workflows/support-bot.yml | 2 +- docs/source/administrator/security.md | 2 +- docs/source/conf.py | 8 ++++---- images/singleuser-sample/README.md | 2 +- jupyterhub/schema.yaml | 11 +---------- jupyterhub/templates/_helpers.tpl | 2 +- tests/test_hub.py | 4 ++-- 7 files changed, 11 insertions(+), 20 deletions(-) diff --git a/.github/workflows/support-bot.yml b/.github/workflows/support-bot.yml index b3828412d1..567e557436 100644 --- a/.github/workflows/support-bot.yml +++ b/.github/workflows/support-bot.yml @@ -21,7 +21,7 @@ jobs: I closed this issue because it was labelled as a support question. - Please help us organize discussion by posting this on the http://discourse.jupyter.org/ forum. If it's your first time posting + Please help us organize discussion by posting this on the https://discourse.jupyter.org/ forum. If it's your first time posting please read https://discourse.jupyter.org/t/getting-good-answers-to-your-questions/1825. The more information you provide the more likely we can help you. diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 3cbbfa7964..61ccd1884e 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -164,7 +164,7 @@ certificates. One options is to use the [Qualys SSL Labs](https://www.ssllabs.co security report generator. Use the following URL structure to test your domain: ``` -http://ssllabs.com/ssltest/analyze.html?d= +https://ssllabs.com/ssltest/analyze.html?d= ``` ## Secure access to Helm diff --git a/docs/source/conf.py b/docs/source/conf.py index bfe40109cf..15386d782b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,14 +24,14 @@ import yaml # -- Sphinx setup function --------------------------------------------------- -# ref: http://www.sphinx-doc.org/en/latest/extdev/tutorial.html#the-setup-function +# ref: https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events def setup(app): app.add_css_file("custom.css") -# -- Referencable variables -------------------------------------------------- +# -- Referenceable variables -------------------------------------------------- def _get_git_ref_from_chartpress_based_version(version): @@ -224,7 +224,7 @@ def parse_schema(d, md=[], depth=0, pre=""): # -- Options for linkcheck builder ------------------------------------------- -# ref: http://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder +# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder linkcheck_ignore = [ r"(.*)github\.com(.*)#", # javascript based anchors r"(.*)/#%21(.*)/(.*)", # /#!forum/jupyter - encoded anchor edge case @@ -243,7 +243,7 @@ def parse_schema(d, md=[], depth=0, pre=""): # -- Options for HTML output ------------------------------------------------- -# ref: http://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output +# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. diff --git a/images/singleuser-sample/README.md b/images/singleuser-sample/README.md index 8063985ac4..f5e59994d7 100644 --- a/images/singleuser-sample/README.md +++ b/images/singleuser-sample/README.md @@ -6,7 +6,7 @@ fundamentals only so that it can get pulled quickly. It is based on the from Project Jupyter's [jupyter/docker-stacks repository](https://github.com/jupyter/docker-stacks) which also contains many other images suitable for use with the Helm chart. To help you choose another one see [the docker-stacks documentation on selecting a -user image](http://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html). +user image](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html). For a brief introduction to _Dockerfiles_, _images_ and _containers_, see [the guide's summary about container technology.](https://z2jh.jupyter.org/en/latest/tools.html#container-technology). diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index e4d70003ec..bd2d082e8d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -111,13 +111,7 @@ properties: ``` If you just want to let all Pods reference an existing secret, use the - `imagePullSecrets` configuration instead. - - To learn the username and password fields to access a gcr.io registry from - a Kubernetes cluster not associated with the same google cloud - credentials, look into [this - guide](http://docs.heptio.com/content/private-registries/pr-gcr.html) and - read the notes about the password. + [`imagePullSecrets`](schema_imagePullSecrets) configuration instead. properties: create: type: boolean @@ -172,9 +166,6 @@ properties: ... } ``` - - Learn more in [this - guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). email: type: [string, "null"] description: | diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl index 88b24a1fea..5cc5e6dee1 100644 --- a/jupyterhub/templates/_helpers.tpl +++ b/jupyterhub/templates/_helpers.tpl @@ -392,7 +392,7 @@ limits: https://github.com/jupyterhub/chartpress#examples-chart-versions-and-image-tags. - The regexReplaceAll function is a sprig library function, see - http://masterminds.github.io/sprig/strings.html. + https://masterminds.github.io/sprig/strings.html. - The regular expression is in golang syntax, but \d had to become \\d for example. diff --git a/tests/test_hub.py b/tests/test_hub.py index d186df0649..cdc549cce4 100644 --- a/tests/test_hub.py +++ b/tests/test_hub.py @@ -1,6 +1,6 @@ """ -These tests commonl use JupyterHub's REST API: -http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyterhub/jupyterhub/HEAD/docs/rest-api.yml +These tests common use JupyterHub's REST API: +https://jupyterhub.readthedocs.io/en/stable/reference/rest-api.html """ import os From 4367794e245eb6518cc2c864e9e194503ed429df Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 28 Jun 2022 05:21:13 +0000 Subject: [PATCH 392/898] Update library/traefik version from v2.7.1 to v2.7.2 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 1bd9bb03bf..89c2bb452a 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -246,7 +246,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.7.1" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.7.2" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 389bd696e68c6fe2050d20951cc0fe726f9ea0dd Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 29 Jun 2022 20:14:50 +0000 Subject: [PATCH 393/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 4f26994c71..41fb972ffc 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -32,7 +32,7 @@ cffi==1.15.0 # via # bcrypt # cryptography -charset-normalizer==2.0.12 +charset-normalizer==2.1.0 # via # aiohttp # requests @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.6.0 +jsonschema==4.6.1 # via # jupyter-telemetry # oauthenticator @@ -159,7 +159,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.28.0 +requests==2.28.1 # via # jupyterhub # mwoauth From 8b48f231aed1e8fd8daa8bf8fbebb193344c5b92 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 30 Jun 2022 07:49:06 +0000 Subject: [PATCH 394/898] Update library/traefik version from v2.7.2 to v2.8.0 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 89c2bb452a..fa9d015462 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -246,7 +246,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.7.2" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.0" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From c270db3328a91f59f60343f5e16d8428f7c557dd Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Fri, 1 Jul 2022 18:24:02 +0200 Subject: [PATCH 395/898] Add custom label support for all resources --- jupyterhub/templates/image-puller/job.yaml | 3 +++ .../templates/scheduling/user-placeholder/statefulset.yaml | 3 +++ .../templates/scheduling/user-scheduler/deployment.yaml | 6 ++++++ 3 files changed, 12 insertions(+) diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml index f9c90ce47d..5509f13e50 100644 --- a/jupyterhub/templates/image-puller/job.yaml +++ b/jupyterhub/templates/image-puller/job.yaml @@ -28,6 +28,9 @@ spec: labels: {{- /* Changes here will cause the Job to restart the pods. */}} {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.prePuller.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} {{- with .Values.prePuller.annotations }} annotations: {{- . | toYaml | nindent 8 }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index 268cddc52e..e928966d69 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -30,6 +30,9 @@ spec: labels: {{- /* Changes here will cause the Deployment to restart the pods. */}} {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.scheduling.userPlaceholder.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} spec: {{- if .Values.scheduling.podPriority.enabled }} priorityClassName: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index ab46ae9c68..09257445d6 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -10,10 +10,16 @@ spec: selector: matchLabels: {{- include "jupyterhub.matchLabels" . | nindent 6 }} + {{- with .Values.scheduling.userScheduler.labels }} + {{- . | toYaml | nindent 6 }} + {{- end }} template: metadata: labels: {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.scheduling.userScheduler.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} annotations: checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} {{- with .Values.scheduling.userScheduler.annotations }} From 5548af8a0f55739dd48dafd006f825f2decf60c7 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 20:01:09 +0100 Subject: [PATCH 396/898] 2.0.0: upgrade guide --- docs/source/administrator/index.md | 1 + docs/source/administrator/upgrade-1-to-2.md | 142 ++++++++++++++++++++ docs/source/administrator/upgrading.md | 47 +++---- 3 files changed, 159 insertions(+), 31 deletions(-) create mode 100644 docs/source/administrator/upgrade-1-to-2.md diff --git a/docs/source/administrator/index.md b/docs/source/administrator/index.md index 043dd74cc7..2db3dd0b11 100644 --- a/docs/source/administrator/index.md +++ b/docs/source/administrator/index.md @@ -15,6 +15,7 @@ services optimization security upgrading +upgrade-1-to-2 ../../jupyterhub/customization troubleshooting advanced diff --git a/docs/source/administrator/upgrade-1-to-2.md b/docs/source/administrator/upgrade-1-to-2.md new file mode 100644 index 0000000000..f55c726682 --- /dev/null +++ b/docs/source/administrator/upgrade-1-to-2.md @@ -0,0 +1,142 @@ +# Major upgrade: 1.\* to 2.\* + +Z2JH 2 contains several breaking changes, including some that affect the security of your deployment. +This guide will help you upgrade from 1.\* to 2.\*. + +## Security: breaking change to `*.networkPolicy.egress` + +NetworkPolicy egress rules have been extended with a new property. +If you have configured any of: + +- `hub.networkPolicy.egress` +- `proxy.chp.networkPolicy.egress` +- `proxy.traefik.networkPolicy.egress` +- `singleuser.networkPolicy.egress` + +you must review your configuration as additional default egress routes have been added. +Previously `...networkPolicy.egress` controlled all egress but a new properties `...networkPolicy.egressAllowRules` add additional egress rules by default. + +If you have configured `...networkPolicy.egress` for `hub`, `proxy.chp`, +`proxy.traefik` or `singleuser` to restrict the permissions to establish +outbound network connections, then this upgrade is likely to _escalate those +permissions unless you revise your configuration_. The new configuration +`...networkPolicy.egressAllowRules` are by default granting most of the egress +permissions previously granted by default via the `...networkPolicy.egress` +configuration, and `...networkPolicy.egress` are now by default not providing +any permissions. + +If you for example had overridden the previously very permissive default value +of `singleuser.networkPolicy.egress` to be less permissive, you should consider +disabling all `singleuser.networkPolicy.egressAllowRules` like this +to not risk escalating the permissions. + +```yaml +singleuser: + networkPolicy: + egressAllowRules: + cloudMetadataServer: false + dnsPortsPrivateIPs: false + nonPrivateIPs: false + privateIPs: false +``` + +For more details, see the documentation on [Kubernetes Network Policies](netpol) +and the configuration reference entries under +[`...networkPolicy.egress`](schema_hub.networkPolicy.egress) and +[`...networkPolicy.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules). + +## JupyterHub 2 and related hub components + +Z2JH 2.0.0 upgrades JupyterHub to the 2.\* series, and also upgrades all hub components. +If you are using any custom JupyterHub services, addons, API integrations, or extra configuration, you should review the breaking changes in the +[JupyterHub 2.0.0 changelog](https://github.com/jupyterhub/jupyterhub/blob/2.3.1/docs/source/changelog.md#200). + +JupyterHub 2 uses an updated database schema. +Z2JH 2.0.0 automatically handles the upgrade, but it will not be possible to downgrade to older releases after this. + +JupyterHub 2 adds RBAC for managing permissions in JupyterHub. +The old permissions model of admin/non-admin still works but you should use +[RBAC to assign the required privileges to users or services in future](https://jupyterhub.readthedocs.io/en/stable/rbac/index.html) + +See +`TODO: link to Notable dependencies updated` +in the changelog for more information on other upgraded hub components. + +## JupyterLab and Jupyter Server + +The default singleuser server is [JupyterLab](https://jupyterlab.readthedocs.io/), running on [Jupyter server](https://jupyter-server.readthedocs.io/en/latest/). +To switch back to Jupyter Notebook either configure/rebuild your singleuser image to default to notebook, or see [the documentation on user interfaces](user-interfaces) + +## Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) + +Z2JH now launches the container's default command (equivalent to setting `CMD` in a `Dockerfile`) instead of overriding it. +This ensures that containers that use a custom start command to configure their environment, such as some +[Jupyter Docker Stacks](https://jupyter-docker-stacks.readthedocs.io/en/latest/) +images, will work without any changes. +To restore the old behaviour set: + +```yaml +singleuser: + cmd: jupyterhub-singleuser +``` + +## Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) + +Previously if `hub.config` was used to configure some JupyterHub traitlets it would override any custom configuration files mounted into `jupyterhub_config.d` in the hub container. +In 2.0.0 all extra customisations (e.g. using `hub.extraConfig` to provide in-line configuration, or `hub.extraFiles` to mount files into `jupyterhub_config.d`) will always take precedence over any Helm chart values. + + +## User scheduler plugin configuration has changed to match `kubescheduler.config.k8s.io/v1beta3` [#2590](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2590) + +Advanced customisation of the user scheduler using plugins now requires Kubernetes 1.21+, and the configuration must follow `kubescheduler.config.k8s.io/v1beta3`. +Customisation is no longer possible with Kubernetes 1.20. + +If you are using the user scheduler without custom plugin configuration you are not affected. + +## Kubernetes version 1.20+ is required [#2635](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2635) + +This Helm chart uses Kubernetes resources that are not available in Kubernetes versions prior to 1.20. + +## `hub.fsGid` is replaced by `hub.podSecurityContext` [#2720](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2720) + +In previous versions of Z2JH `hub.fsGid` set a supplemental group ID, which is required on some K8s systems to ensure JupyterHub has permissions to read/write files on a volume. +This has been replaced by the more general [`hub.podSecurityContext`](schema_hub.podSecurityContext). +To upgrade set: + +```yaml +hub: + podSecurityContext: + fsGroup: GROUP-ID +``` + +## Hub image is based on Debian instead of Ubuntu [#2733](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2733) + +The hub container base image has switched from `ubuntu:20.04` to `python:3.9-slim-bullseye` which is based on `debian:bullseye-slim`. +If you have extended the Z2JH hub image please review the [hub Dockerfile](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/2.0.0/images/hub/Dockerfile). +Note the singleuser image is not affected. + +## Disabling RBAC requires setting multiple properties,`rbac.enable` is removed [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) + +If you previously disabled RBAC using `rbac.enable: False` you should set + +```yaml +rbac: + create: False +hub: + serviceAccount: + create: false +proxy: + traefik: + serviceAccount: + create: false +scheduling: + userScheduler: + serviceAccount: + create: false +prePuller: + hook: + serviceAccount: + create: false +``` + +When you have updated your configuration follow the rest of the [upgrade guide](upgrading). diff --git a/docs/source/administrator/upgrading.md b/docs/source/administrator/upgrading.md index 1aa189fcdb..36b7ef3275 100644 --- a/docs/source/administrator/upgrading.md +++ b/docs/source/administrator/upgrading.md @@ -16,9 +16,10 @@ or the [Discourse forum](https://discourse.jupyter.org/). ## Major helm-chart upgrades These steps are **critical** before performing a major upgrade. +Z2JH follows semantic versioning, so major upgrades are indicated by an increase in the first component of the version. 1. Always backup your database! -2. Review the [CHANGELOG](changelog) for incompatible changes and upgrade instructions. +2. Review the [CHANGELOG](changelog) and [2.0.0 upgrade guide](upgrade-1-to-2) for incompatible changes and upgrade instructions. 3. Update your configuration accordingly. 4. User servers may need be stopped prior to the upgrade, or restarted after it. @@ -26,32 +27,10 @@ These steps are **critical** before performing a major upgrade. we recommend you test the upgrade out on a staging cluster first before applying it to production. -### v0.5 to v0.6 - -See the [CHANGELOG](changelog). - -### v0.4 to v0.5 - -Release 0.5 contains a major JupyterHub version bump (from 0.7.2 to 0.8). -Since it is a major upgrade of JupyterHub that changes how authentication is -implemented, user servers must be stopped during the upgrade. -The database schema has also changed, so a database upgrade must be performed. - -See the [CHANGELOG](changelog) for this release for more information about -changes. - -## Subtopics - -This section covers upgrade information specific to the following: - -- `helm upgrade` command -- Databases -- RBAC (Role Based Access Control) -- Custom Docker images (helm-upgrade-command)= -### `helm upgrade` command +## `helm upgrade` command After modifying your `config.yaml` file according to the CHANGELOG, you will need `` to run the upgrade commands. To find ``, run: @@ -75,10 +54,10 @@ For example, to upgrade to version `1.1.1` with a helm release name of `jhub` in helm upgrade --cleanup-on-fail jhub jupyterhub/jupyterhub --version=1.1.1 --values config.yaml --namespace jhub ``` -### Database +## Database -This release contains a major JupyterHub version bump (from 0.7.2 to 0.8). If -you are using the default database provider (SQLite), then the required db upgrades +Major releases of Z2JH may include a major release of JupyterHub that requires an upgrade of the database schema. +If you are using the default database provider (SQLite), then the required db upgrades will be performed automatically when you do a `helm upgrade`. **Default (SQLite)**: The database upgrade will be performed automatically when you @@ -100,18 +79,24 @@ will be performed automatically when you do a `helm upgrade`. 4. Do a [`helm upgrade`](helm-upgrade-command). This should perform the database upgrade needed. 5. Remove the lines added in step 3, and do another [`helm upgrade`](helm-upgrade-command). -### Custom Docker Images: JupyterHub version match +## Custom Docker Images: JupyterHub version match If you are using a custom built image, make sure that the version of the -JupyterHub package installed in it is now 0.8.1. It needs to be 0.8.1 for it to work with -v0.6 of the helm chart. +JupyterHub package installed in it matches the major version of JupyterHub, current 2.\*. For example, if you are using `pip` to install JupyterHub in your custom Docker Image, you would use: ```Dockerfile -RUN pip install --no-cache-dir jupyterhub==0.8.1 +RUN pip install --no-cache-dir jupyterhub==2.3.1 ``` +If you are using conda or mamba: +```Dockerfile +RUN conda install -y jupyterhub-base=2.3.1 +``` + +Update the configuration to use this new image, which is typically done via +`singleuser.image` or as part of `singleuser.profileList`. ## JupyterHub versions installed in each Helm Chart From cf3f1b88f29e8a1294d74563d5f1b9d266dd514c Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 20:01:32 +0100 Subject: [PATCH 397/898] Add `(netpol)` target --- docs/source/administrator/security.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 61ccd1884e..3d74daf5d1 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -283,6 +283,8 @@ singleuser: ip: 169.254.169.254 ``` +(netpol)= + ## Kubernetes Network Policies ```{warning} From 241b25f09891f099174d7a7a31c7c31a7af530ce Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 20:01:43 +0100 Subject: [PATCH 398/898] Remove `(upgrading)` label, since it duplicates the filename --- docs/source/administrator/upgrading.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/source/administrator/upgrading.md b/docs/source/administrator/upgrading.md index 36b7ef3275..3f2b7e9df6 100644 --- a/docs/source/administrator/upgrading.md +++ b/docs/source/administrator/upgrading.md @@ -1,5 +1,3 @@ -(upgrading)= - # Upgrading your Helm chart This page covers best-practices in upgrading your JupyterHub deployment via updates From 00c14a0d0f33f206108a620353565af6f82236fb Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 22:33:48 +0100 Subject: [PATCH 399/898] Move upgrade pages into new folder --- docs/source/administrator/index.md | 3 +-- .../administrator/{upgrading.md => upgrading/index.md} | 7 +++++++ .../source/administrator/{ => upgrading}/upgrade-1-to-2.md | 0 3 files changed, 8 insertions(+), 2 deletions(-) rename docs/source/administrator/{upgrading.md => upgrading/index.md} (98%) rename docs/source/administrator/{ => upgrading}/upgrade-1-to-2.md (100%) diff --git a/docs/source/administrator/index.md b/docs/source/administrator/index.md index 2db3dd0b11..c8aa06b419 100644 --- a/docs/source/administrator/index.md +++ b/docs/source/administrator/index.md @@ -14,8 +14,7 @@ authentication services optimization security -upgrading -upgrade-1-to-2 +upgrading/index ../../jupyterhub/customization troubleshooting advanced diff --git a/docs/source/administrator/upgrading.md b/docs/source/administrator/upgrading/index.md similarity index 98% rename from docs/source/administrator/upgrading.md rename to docs/source/administrator/upgrading/index.md index 3f2b7e9df6..da372fb016 100644 --- a/docs/source/administrator/upgrading.md +++ b/docs/source/administrator/upgrading/index.md @@ -3,6 +3,13 @@ This page covers best-practices in upgrading your JupyterHub deployment via updates to the Helm Chart. +```{toctree} +:maxdepth: 1 +:caption: Upgrade Guide + +upgrade-1-to-2 +``` + Upgrading from one version of the Helm Chart to the next should be as seamless as possible, and generally shouldn't require major changes to your deployment. Check the [CHANGELOG](changelog) diff --git a/docs/source/administrator/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md similarity index 100% rename from docs/source/administrator/upgrade-1-to-2.md rename to docs/source/administrator/upgrading/upgrade-1-to-2.md From 6f7649d75d23fdaf98111e2dceabe7914182ef40 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 23:01:05 +0100 Subject: [PATCH 400/898] Fix upgrading redirect --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 55bf219ff0..4b647f5734 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -160,7 +160,7 @@ def _get_git_ref_from_chartpress_based_version(version): "tips": "community/tips", "index-community-resources": "resources/community", "additional-resources": "resources/community", - "upgrading": "administrator/upgrading", + "upgrading": "administrator/upgrading/index", "troubleshooting": "administrator/troubleshooting", "security": "administrator/security", "optimization": "administrator/optimization", From d7e9e6a99f70a1d493d43180e07f76564d7097a9 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 22:53:44 +0100 Subject: [PATCH 401/898] Rewrite parts of upgrade guide for 2.0.0 --- docs/source/administrator/upgrading/index.md | 42 ++++++++++--------- .../administrator/upgrading/upgrade-1-to-2.md | 5 +-- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/docs/source/administrator/upgrading/index.md b/docs/source/administrator/upgrading/index.md index da372fb016..d19f94bd88 100644 --- a/docs/source/administrator/upgrading/index.md +++ b/docs/source/administrator/upgrading/index.md @@ -1,38 +1,40 @@ -# Upgrading your Helm chart +# Upgrading JupyterHub for Kubernetes -This page covers best-practices in upgrading your JupyterHub deployment via updates +This section covers best-practices in upgrading your JupyterHub deployment via updates to the Helm Chart. -```{toctree} -:maxdepth: 1 -:caption: Upgrade Guide - -upgrade-1-to-2 -``` +Z2JH follows [semantic versioning](https://semver.org/), with each version taking the form `MAJOR.MINOR.PATCH`. +Minor and patch releases should be backwards compatible, and shouldn't require changes to your deployment. +Review the [CHANGELOG](changelog) to find out about new features or bug fixes that affect your deployment, +then follow [](helm-upgrade-command). -Upgrading from one version of the Helm Chart to the -next should be as seamless as possible, and generally shouldn't require major -changes to your deployment. Check the [CHANGELOG](changelog) -for each release to find out if there are any breaking changes in the newest version. +Major releases may contain breaking changes, and will often require changes to your configuration. +They have dedicated instructions for upgrading your deployment in addition to the general instructions on this page. For additional help, feel free to reach out to us on [gitter](https://gitter.im/jupyterhub/jupyterhub) or the [Discourse forum](https://discourse.jupyter.org/). +(upgrading-major-upgrades)= + ## Major helm-chart upgrades +```{toctree} +:maxdepth: 1 +:caption: Major releases guides + +upgrade-1-to-2 +``` + These steps are **critical** before performing a major upgrade. -Z2JH follows semantic versioning, so major upgrades are indicated by an increase in the first component of the version. 1. Always backup your database! -2. Review the [CHANGELOG](changelog) and [2.0.0 upgrade guide](upgrade-1-to-2) for incompatible changes and upgrade instructions. +2. Review the appropriate upgrade guide, and/or the [CHANGELOG](changelog) for incompatible changes and upgrade instructions. 3. Update your configuration accordingly. -4. User servers may need be stopped prior to the upgrade, - or restarted after it. +4. User servers may need be stopped prior to the upgrade, or restarted after it. 5. If you are planning an upgrade of a critical major installation, we recommend you test the upgrade out on a staging cluster first before applying it to production. - (helm-upgrade-command)= ## `helm upgrade` command @@ -95,9 +97,11 @@ you would use: ```Dockerfile RUN pip install --no-cache-dir jupyterhub==2.3.1 ``` + If you are using conda or mamba: + ```Dockerfile -RUN conda install -y jupyterhub-base=2.3.1 +RUN conda install --channel=conda-forge -y jupyterhub-base=2.3.1 ``` Update the configuration to use this new image, which is typically done via @@ -118,4 +122,4 @@ deleting the helm chart using: helm delete --namespace ``` -`helm list --namespace ` may be used to find . +`helm list --namespace ` may be used to find ``. diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index f55c726682..fd1db353c4 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -85,7 +85,6 @@ singleuser: Previously if `hub.config` was used to configure some JupyterHub traitlets it would override any custom configuration files mounted into `jupyterhub_config.d` in the hub container. In 2.0.0 all extra customisations (e.g. using `hub.extraConfig` to provide in-line configuration, or `hub.extraFiles` to mount files into `jupyterhub_config.d`) will always take precedence over any Helm chart values. - ## User scheduler plugin configuration has changed to match `kubescheduler.config.k8s.io/v1beta3` [#2590](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2590) Advanced customisation of the user scheduler using plugins now requires Kubernetes 1.21+, and the configuration must follow `kubescheduler.config.k8s.io/v1beta3`. @@ -115,7 +114,7 @@ The hub container base image has switched from `ubuntu:20.04` to `python:3.9-sli If you have extended the Z2JH hub image please review the [hub Dockerfile](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/2.0.0/images/hub/Dockerfile). Note the singleuser image is not affected. -## Disabling RBAC requires setting multiple properties,`rbac.enable` is removed [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) +## Disabling RBAC requires setting multiple properties, `rbac.enable` is removed [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) If you previously disabled RBAC using `rbac.enable: False` you should set @@ -139,4 +138,4 @@ prePuller: create: false ``` -When you have updated your configuration follow the rest of the [upgrade guide](upgrading). +When you have updated your configuration follow the rest of the [upgrade guide](upgrading-major-upgrades). From deeb5e72cb1e83cbfebf84baaac67d97864941f4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 3 Jul 2022 22:55:26 +0100 Subject: [PATCH 402/898] Use `*.` instead of `...` for `...networkPolicy.egress` --- .../administrator/upgrading/upgrade-1-to-2.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index fd1db353c4..88c976bd43 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -14,15 +14,15 @@ If you have configured any of: - `singleuser.networkPolicy.egress` you must review your configuration as additional default egress routes have been added. -Previously `...networkPolicy.egress` controlled all egress but a new properties `...networkPolicy.egressAllowRules` add additional egress rules by default. +Previously `*.networkPolicy.egress` controlled all egress but a new properties `*.networkPolicy.egressAllowRules` add additional egress rules by default. -If you have configured `...networkPolicy.egress` for `hub`, `proxy.chp`, +If you have configured `*.networkPolicy.egress` for `hub`, `proxy.chp`, `proxy.traefik` or `singleuser` to restrict the permissions to establish outbound network connections, then this upgrade is likely to _escalate those permissions unless you revise your configuration_. The new configuration -`...networkPolicy.egressAllowRules` are by default granting most of the egress -permissions previously granted by default via the `...networkPolicy.egress` -configuration, and `...networkPolicy.egress` are now by default not providing +`*.networkPolicy.egressAllowRules` are by default granting most of the egress +permissions previously granted by default via the `*.networkPolicy.egress` +configuration, and `*.networkPolicy.egress` are now by default not providing any permissions. If you for example had overridden the previously very permissive default value @@ -42,8 +42,8 @@ singleuser: For more details, see the documentation on [Kubernetes Network Policies](netpol) and the configuration reference entries under -[`...networkPolicy.egress`](schema_hub.networkPolicy.egress) and -[`...networkPolicy.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules). +[`*.networkPolicy.egress`](schema_hub.networkPolicy.egress) and +[`*.networkPolicy.egressAllowRules`](schema_hub.networkPolicy.egressAllowRules). ## JupyterHub 2 and related hub components From 4c23182a4307f74b25ddc51c182e8b7187451c82 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 05:05:44 +0000 Subject: [PATCH 403/898] build(deps): bump aquasecurity/trivy-action from 0.5.0 to 0.5.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/7b7aa264d83dc58691451798b4d117d53d21edfe...0105373003c89c494a3f436bd5efc57f3ac1ca20) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 23b68d155c..06b31fda1a 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe + uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe + uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@7b7aa264d83dc58691451798b4d117d53d21edfe + uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 with: image-ref: rebuilt-image format: table From 65c19f31e08add534cc6ae6db5e4e0ec01120677 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 4 Jul 2022 05:28:30 +0000 Subject: [PATCH 404/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 525f5683f8..5667de081e 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-06-27_05:29:26 +# VULN_SCAN_TIME=2022-07-04_05:28:29 USER root RUN apt-get update \ From 30e9d546da7ee3b3dc8b503e2748cd9bea49e20c Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Mon, 4 Jul 2022 08:42:38 +0200 Subject: [PATCH 405/898] Remove custom labels from matchlabels --- jupyterhub/templates/scheduling/user-scheduler/deployment.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 09257445d6..ef41b4b86b 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -10,9 +10,6 @@ spec: selector: matchLabels: {{- include "jupyterhub.matchLabels" . | nindent 6 }} - {{- with .Values.scheduling.userScheduler.labels }} - {{- . | toYaml | nindent 6 }} - {{- end }} template: metadata: labels: From 8143421e57033670335638da734247a1e7f464bc Mon Sep 17 00:00:00 2001 From: Simon Li Date: Mon, 4 Jul 2022 11:49:30 +0100 Subject: [PATCH 406/898] 1-2 upgrade: only sqlite db upgrade automatic --- docs/source/administrator/upgrading/upgrade-1-to-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 88c976bd43..6643f8c265 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -52,7 +52,7 @@ If you are using any custom JupyterHub services, addons, API integrations, or ex [JupyterHub 2.0.0 changelog](https://github.com/jupyterhub/jupyterhub/blob/2.3.1/docs/source/changelog.md#200). JupyterHub 2 uses an updated database schema. -Z2JH 2.0.0 automatically handles the upgrade, but it will not be possible to downgrade to older releases after this. +Z2JH 2.0.0 automatically handles the upgrade for SQLite databases (the default), but it will not be possible to downgrade to older releases after this. JupyterHub 2 adds RBAC for managing permissions in JupyterHub. The old permissions model of admin/non-admin still works but you should use From b747f55ab1adb1b8b6826f23e075aad93d78060d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 4 Jul 2022 15:41:11 +0200 Subject: [PATCH 407/898] docs: fix typo and add note about hub.db.upgrade --- docs/source/administrator/upgrading/upgrade-1-to-2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 6643f8c265..92957cf657 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -14,7 +14,7 @@ If you have configured any of: - `singleuser.networkPolicy.egress` you must review your configuration as additional default egress routes have been added. -Previously `*.networkPolicy.egress` controlled all egress but a new properties `*.networkPolicy.egressAllowRules` add additional egress rules by default. +Previously `*.networkPolicy.egress` controlled all egress but a new property `*.networkPolicy.egressAllowRules` add additional egress rules by default. If you have configured `*.networkPolicy.egress` for `hub`, `proxy.chp`, `proxy.traefik` or `singleuser` to restrict the permissions to establish @@ -52,7 +52,7 @@ If you are using any custom JupyterHub services, addons, API integrations, or ex [JupyterHub 2.0.0 changelog](https://github.com/jupyterhub/jupyterhub/blob/2.3.1/docs/source/changelog.md#200). JupyterHub 2 uses an updated database schema. -Z2JH 2.0.0 automatically handles the upgrade for SQLite databases (the default), but it will not be possible to downgrade to older releases after this. +Z2JH 2.0.0 automatically handles the upgrade for SQLite databases (the default), but if you use an external database you need to configure [`hub.db.upgrade`](schema_hub.db.upgrade) to true when upgrading. It will not be possible to downgrade to older releases after this without also using a backup or resetting the database. JupyterHub 2 adds RBAC for managing permissions in JupyterHub. The old permissions model of admin/non-admin still works but you should use From 47bf1d6be9d60718525d10a8ac235f51eaf43efe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 22:35:18 +0000 Subject: [PATCH 408/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.3.0 → 22.6.0](https://github.com/psf/black/compare/22.3.0...22.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8121811ce..9b2ba962b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.6.0 hooks: - id: black args: From 0421ef76951b4e79c0e11c4247c2282b866a4192 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 19:52:47 +0100 Subject: [PATCH 409/898] ci/common: use generic `await_kubectl_rollout` --- ci/common | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/ci/common b/ci/common index c28d82afc7..71c6d34aa8 100755 --- a/ci/common +++ b/ci/common @@ -13,22 +13,26 @@ setup_helm () { curl -sf https://raw.githubusercontent.com/helm/helm/HEAD/scripts/get-helm-3 | DESIRED_VERSION=${HELM_VERSION} bash } +await_kubectl_rollout() { + kubectl rollout status --watch --timeout 300s "$1" +} + await_pebble() { - kubectl rollout status --watch --timeout 300s deployment/pebble \ - && kubectl rollout status --watch --timeout 300s deployment/pebble-coredns + await_kubectl_rollout deployment/pebble \ + && await_kubectl_rollout deployment/pebble-coredns } await_jupyterhub() { - kubectl rollout status --watch --timeout 300s deployment/proxy \ - && kubectl rollout status --watch --timeout 300s deployment/hub \ + await_kubectl_rollout deployment/proxy \ + && await_kubectl_rollout deployment/hub \ && ( if kubectl get deploy/autohttps &> /dev/null; then - kubectl rollout status --watch --timeout 300s deployment/autohttps + await_kubectl_rollout deployment/autohttps fi ) \ && ( if kubectl get deploy/user-scheduler &> /dev/null; then - kubectl rollout status --watch --timeout 300s deployment/user-scheduler + await_kubectl_rollout deployment/user-scheduler fi ) } From 3a287bb5f00b8a9147dff58f56bdce3fb16550ff Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 19:54:52 +0100 Subject: [PATCH 410/898] Test postgres db schema upgrade --- .github/workflows/test-chart.yaml | 61 +++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 5a88e73293..cf7f612b62 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -146,16 +146,18 @@ jobs: --set hub.config.CryptKeeper.keys[0]=cccc3333 create-k8s-test-resources: true - # We run two upgrade tests where we first install an already released - # Helm chart version and then upgrades to the version we are now - # testing. We test upgrading from the latest stable version (like - # 1.2.3), and one from the latest dev version (like - # 1.2.3-n012.h1234abc). + # We run three upgrade tests where we first install an already released + # Helm chart version and then upgrade to the version we are now + # testing: + # - latest stable version (like 1.2.3) + # - latest dev version (like 1.2.3-n012.h1234abc), + # - and an old version that requires a JupyterHub DB upgrade # # It can be very useful to see the "Helm diff" step's output from the # latest dev version. # - # The upgrade-from input should match the version information from + # The upgrade-from input should be a chart version, or match the version + # information from # https://jupyterhub.github.io/helm-chart/info.json # - k3s-channel: v1.23 @@ -181,6 +183,34 @@ jobs: local-chart-extra-args: >- --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic + - k3s-channel: v1.24 + test: upgrade + # We're testing hub.db.upgrade with PostgreSQL so this version must be old + # enough to require a DB upgrade + upgrade-from: 1.2.0 + upgrade-from-extra-args: >- + --set proxy.secretToken=aaaa1111 + --set hub.cookieSecret=bbbb2222 + --set hub.config.CryptKeeper.keys[0]=cccc3333 + --set hub.db.type=postgres + --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub + --set singleuser.storage.type=dynamic + local-chart-extra-args: >- + --set hub.db.type=postgres + --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub + --set singleuser.storage.type=dynamic + --set hub.db.upgrade=true + create-k8s-test-resources: true + # https://artifacthub.io/packages/helm/bitnami/postgresql + setup-postgresql-args: >- + --version=11.6.13 + --set auth.enablePostgresUser=true + --set auth.postgresPassword=postgres + --set auth.database=jupyterhub + --set audit.logHostname=true + --set audit.logConnections=true + --set audit.logDisconnections=true + --set audit.clientMinMessages=debug steps: - uses: actions/checkout@v3 @@ -237,11 +267,23 @@ jobs: timeout: 150 max-restarts: 1 + - name: "(Upgrade) Install PostgreSQL server chart" + if: matrix.setup-postgresql-args + run: | + . ./ci/common + helm repo add bitnami https://charts.bitnami.com/bitnami + helm install postgresql bitnami/postgresql ${{ matrix.setup-postgresql-args }} + await_kubectl_rollout statefulset/postgresql + - name: "(Upgrade) Install ${{ matrix.upgrade-from }} chart" if: matrix.test == 'upgrade' run: | . ./ci/common - UPGRADE_FROM_VERSION=$(curl -sS https://jupyterhub.github.io/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') + if [ ${{ matrix.upgrade-from }} = stable -o ${{ matrix.upgrade-from }} = dev ]; then + UPGRADE_FROM_VERSION=$(curl -sS https://jupyterhub.github.io/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') + else + UPGRADE_FROM_VERSION=${{ matrix.upgrade-from }} + fi echo "UPGRADE_FROM_VERSION=$UPGRADE_FROM_VERSION" >> $GITHUB_ENV echo "" @@ -333,3 +375,8 @@ jobs: if: always() with: important-workloads: deploy/hub deploy/proxy + + - name: Debug PostgreSQL logs on failure + if: failure() && matrix.setup-postgresql-args + run: | + kubectl logs statefulset/postgresql From 4b35bc2d96653f260a0d8670083d25e06d9f9614 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 19:55:11 +0100 Subject: [PATCH 411/898] Allow outbound 5432 in hub netpol --- dev-config.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-config.yaml b/dev-config.yaml index d92d4e8e71..cd8795bb7d 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -51,10 +51,13 @@ hub: test-generation-of-apiToken: {} networkPolicy: egress: # overrides allowance of 0.0.0.0/0 - # In kind/k3s clusters the Kubernetes API server is exposing this port - ports: + # In kind/k3s clusters the Kubernetes API server is exposing this port - protocol: TCP port: 6443 + # For testing postgres + - protocol: TCP + port: 5432 resources: requests: memory: 0 From 94a94d181b26139cb26ba9e472a36361256ae813 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 20:10:43 +0100 Subject: [PATCH 412/898] Explain why db upgrade isn't automated for PostgreSQL/MySQL --- docs/source/administrator/upgrading/index.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/administrator/upgrading/index.md b/docs/source/administrator/upgrading/index.md index d19f94bd88..d0c4905f0a 100644 --- a/docs/source/administrator/upgrading/index.md +++ b/docs/source/administrator/upgrading/index.md @@ -66,6 +66,9 @@ helm upgrade --cleanup-on-fail jhub jupyterhub/jupyterhub --version=1.1.1 --valu Major releases of Z2JH may include a major release of JupyterHub that requires an upgrade of the database schema. If you are using the default database provider (SQLite), then the required db upgrades will be performed automatically when you do a `helm upgrade`. +A backup of the old database is automatically created on the hub volume. + +It is not possible to automatically backup other database providers, so the upgrade is not done automatically. **Default (SQLite)**: The database upgrade will be performed automatically when you [perform the upgrade](helm-upgrade-command) @@ -84,7 +87,7 @@ will be performed automatically when you do a `helm upgrade`. ``` 4. Do a [`helm upgrade`](helm-upgrade-command). This should perform the database upgrade needed. -5. Remove the lines added in step 3, and do another [`helm upgrade`](helm-upgrade-command). +5. Remove the lines added in step 3, and do another [`helm upgrade`](helm-upgrade-command) so that future JupyterHub upgrades don't inadvertently upgrade the schema. ## Custom Docker Images: JupyterHub version match From b29f2be0e87929533d36a8e8a4a96ac48958083d Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 20:30:01 +0100 Subject: [PATCH 413/898] Adjust kerning on large JupyterHub in NOTES.txt --- jupyterhub/templates/NOTES.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index e7509ff6e8..e9a4edfcd4 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -1,12 +1,12 @@ {{- $proxy_service := include "jupyterhub.proxy-public.fullname" . -}} {{- /* Generated with https://patorjk.com/software/taag/#p=display&h=0&f=Slant&t=JupyterHub */}} -. __ __ __ __ __ - / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ - __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ -/ /_/ / / /_/ / / /_/ / / /_/ / / /_ / __/ / / / __ / / /_/ / / /_/ / -\____/ \__,_/ / .___/ \__, / \__/ \___/ /_/ /_/ /_/ \__,_/ /_.___/ - /_/ /____/ +. __ __ __ __ __ + / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ + __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ +/ /_/ / / /_/ / / /_/ / / /_/ / / /_ / __/ / / / __ / / /_/ / / /_/ / +\____/ \__,_/ / .___/ \__, / \__/ \___/ /_/ /_/ /_/ \__,_/ /_.___/ + /_/ /____/ You have successfully installed the official JupyterHub Helm chart! From b3592a45d1558ada2d515add2714109068c06db4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 20:45:34 +0100 Subject: [PATCH 414/898] Fix `redirected permanently` linkcheck apart from changelog --- docs/source/administrator/authentication.md | 6 +++--- docs/source/administrator/optimization.md | 2 +- docs/source/kubernetes/ibm/step-zero-ibm.md | 2 +- docs/source/kubernetes/microsoft/step-zero-azure.md | 4 ++-- docs/source/kubernetes/ovh/step-zero-ovh.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 65e33e59e1..21694d3b4e 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -192,11 +192,11 @@ hub: ``` ```{admonition} About the choice of scope -The narrower scope `read:user` is sufficient for a configuration of `allowed_organizations` to function if you both list only entire organizations rather than specific teams, and if the users [make their organization membership public](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership). +The narrower scope `read:user` is sufficient for a configuration of `allowed_organizations` to function if you both list only entire organizations rather than specific teams, and if the users [make their organization membership public](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/publicizing-or-hiding-organization-membership). -The broader scope `read:org` doesn't have the limitations of `read:user`, but will require a one-off approval by the admins of the GitHub organizations' listed in `allowed_organizations`. This kind of approval can be requested by organization users [as documented on GitHub](https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/managing-your-membership-in-organizations/requesting-organization-approval-for-oauth-apps). +The broader scope `read:org` doesn't have the limitations of `read:user`, but will require a one-off approval by the admins of the GitHub organizations' listed in `allowed_organizations`. This kind of approval can be requested by organization users [as documented on GitHub](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/requesting-organization-approval-for-oauth-apps). -For details about GitHub scopes, see [GitHub's documentation](https://docs.github.com/en/developers/apps/scopes-for-oauth-apps). +For details about GitHub scopes, see [GitHub's documentation](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps). ``` #### Google diff --git a/docs/source/administrator/optimization.md b/docs/source/administrator/optimization.md index 75a392b2e9..a3eaaf55e7 100644 --- a/docs/source/administrator/optimization.md +++ b/docs/source/administrator/optimization.md @@ -189,7 +189,7 @@ waiting time for the pod, and as a pod can represent a user, it can lead to a long waiting time for a user. There are now options to address this. With Kubernetes 1.11+ (that requires Helm 2.11+), [Pod Priority and -Preemption](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/) +Preemption](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) was introduced. This allows pods with higher priority to preempt / evict pods with lower priority if that would help the higher priority pod fit on a node. diff --git a/docs/source/kubernetes/ibm/step-zero-ibm.md b/docs/source/kubernetes/ibm/step-zero-ibm.md index 5ed4ac6fa5..b92e35960d 100644 --- a/docs/source/kubernetes/ibm/step-zero-ibm.md +++ b/docs/source/kubernetes/ibm/step-zero-ibm.md @@ -48,7 +48,7 @@ Procedure: Or, if you prefer, create the cluster using the [IBM Cloud CLI tools](https://cloud.ibm.com/docs/containers?topic=containers-cs_cli_install)) 2. Configure kubectl - [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) is a CLI tool to interact with a Kubernetes cluster. In this occasion, you will use it to point forward to the created Kubernetes cluster. + [kubectl](https://kubernetes.io/docs/reference/kubectl/) is a CLI tool to interact with a Kubernetes cluster. In this occasion, you will use it to point forward to the created Kubernetes cluster. 1. Use `ibmcloud login` to log in interactively into the IBM Cloud. Provide the organization (org), location and space under which the cluster is created. You can reconfirm the details by running `ibmcloud target` command. 2. When the cluster is ready, retrieve the cluster configuration by using the cluster's name: diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index aca090d839..1865538477 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -5,7 +5,7 @@ You can create a Kubernetes cluster [either through the Azure portal website, or using the Azure command line tools](https://docs.microsoft.com/en-us/azure/aks/). This page describes the commands required to setup a Kubernetes cluster using the command line. -If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal). +If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal). 1. Prepare your Azure shell environment. You have two options, one is to use the Azure interactive shell, the other is to install the Azure command-line @@ -122,7 +122,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta To enable this in Azure, we must first create a [Virtual Network](https://docs.microsoft.com/en-gb/azure/virtual-network/virtual-networks-overview) with Azure's own network policies enabled. - This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://docs.projectcalico.org) network policies. + This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://projectcalico.docs.tigera.io/) network policies. ``` az network vnet create \ diff --git a/docs/source/kubernetes/ovh/step-zero-ovh.md b/docs/source/kubernetes/ovh/step-zero-ovh.md index c8fe419f3c..228edb114f 100644 --- a/docs/source/kubernetes/ovh/step-zero-ovh.md +++ b/docs/source/kubernetes/ovh/step-zero-ovh.md @@ -2,7 +2,7 @@ # Kubernetes on [OVHcloud](https://www.ovh.ie/) (OVH) -[OVHcloud](https://www.ovh.ie/) is a leader in the hosted private cloud services space in Europe. +[OVHcloud](https://www.ovh.com/) is a leader in the hosted private cloud services space in Europe. They offer a managed Kubernetes service as well as a managed private registry for Docker images. From 53753aea05d85785f5dd32002e18c9cdb4566f9b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 6 Jul 2022 23:20:24 +0100 Subject: [PATCH 415/898] ci postgres: use `--repo` instead of `helm repo add` Co-authored-by: Erik Sundell --- .github/workflows/test-chart.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index cf7f612b62..437316f83b 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -271,8 +271,7 @@ jobs: if: matrix.setup-postgresql-args run: | . ./ci/common - helm repo add bitnami https://charts.bitnami.com/bitnami - helm install postgresql bitnami/postgresql ${{ matrix.setup-postgresql-args }} + helm install postgresql postgresql --repo=https://charts.bitnami.com/bitnami ${{ matrix.setup-postgresql-args }} await_kubectl_rollout statefulset/postgresql - name: "(Upgrade) Install ${{ matrix.upgrade-from }} chart" From 5b12874ddec7abda53d5a9ad3ad6cffc61304068 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 7 Jul 2022 11:33:18 +0200 Subject: [PATCH 416/898] docs: fix broken links in changelog --- docs/source/changelog.md | 50 +++++++++---------- .../microsoft/step-zero-azure-autoscale.md | 4 +- .../kubernetes/redhat/step-zero-openshift.md | 4 +- images/image-awaiter/README.md | 2 +- images/singleuser-sample/README.md | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index a2359f2ce7..938de3dcc6 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -282,7 +282,7 @@ and small bugfixes will increment the three version numbers. The documentation for how to setup a Amazon EKS cluster included an insecure step that would give anyone access to the Kubernetes cluster. If you have followed these instructions between `0.7.0-beta.1` and `0.11.1`, please see the -[this post in the Jupyter forum](https://discourse.jupyter.org/t/-/9372). +[this post in the Jupyter forum](https://discourse.jupyter.org/t/critical-security-vulnerability-in-instructions-on-z2jh-jupyter-org-to-set-up-a-amazon-eks-based-k8s-cluster/9372). #### Breaking changes @@ -1421,7 +1421,7 @@ which are forbidden otherwise. If you encounter issues with upgrades, check for changed configuration in this document, and make sure your config is up to date. If you aren't able to get the upgrade to work, -you can [rollback](https://docs.helm.sh/helm/#helm-rollback) +you can [rollback](https://helm.sh/docs/helm/helm_rollback/) to a previous version with: helm rollback $RELEASE @@ -1434,7 +1434,7 @@ if you have problems or questions. ##### Easier user-selectable profiles upon login Profile information is now passed through to KubeSpawner. This means you can -[specify multiple user profiles that users can select from](https://zero-to-jupyterhub.readthedocs.io/en/latest/user-environment.html?highlight=profile#allow-users-to-create-their-own-conda-environments) +[specify multiple user profiles that users can select from](schema_singleuser.profileList) when they log in. ([#402](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/402)) ##### Configurable image pull secrets @@ -1456,7 +1456,7 @@ Want to make your autoscheduler work efficiently? Then you should schedule pods - **Pod priority and User placeholders** - #929 -Want to scale up before users arrive so they don't end up waiting for the node to pull an image of several gigabytes in size? By adding a configurable fixed amount of user placeholder pods with a lower [pod priority](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/) than real user pods, we can accomplish this. It requires k8s v1.11 though. +Want to scale up before users arrive so they don't end up waiting for the node to pull an image of several gigabytes in size? By adding a configurable fixed amount of user placeholder pods with a lower [pod priority](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) than real user pods, we can accomplish this. It requires k8s v1.11 though. - **preferScheduleNextToRealUsers - improves autoscaling** - #930 This setting slightly improves the ability for a cluster autoscaler to scale down by increasing the likelihood of user placeholders being left alone on a node rather than real users. Real users can't be moved around while user placeholder pods can @@ -2792,7 +2792,7 @@ prePuller: enabled: false ``` -See the [pre-puller docs](http://zero-to-jupyterhub.readthedocs.io/en/latest/advanced.html#pre-pulling-images-for-faster-startup) for more info! +See the [pre-puller docs](pulling-images-before-users-arrive) for more info! #### Upgrading from 0.5 @@ -2806,7 +2806,7 @@ by running `helm list`. ##### Troubleshooting If your helm upgrade fails due to the error `no Ingress with the name "jupyterhub-internal" found`, -you may be experiencing a [helm bug](https://github.com/kubernetes/helm/issues/3275). To work +you may be experiencing a [helm bug](https://github.com/helm/helm/issues/3275). To work around this, run `kubectl --namespace= delete ingress jupyterhub-internal` and re-run the `helm upgrade` command. Note that this will cause a short unavailability of your hub over HTTPS, which will resume normal availability once the deployment upgrade completes. @@ -2819,7 +2819,7 @@ z2jh is more secure by default with 0.6. We now block access to cloud security metadata endpoints by default. -See the [security documentation](http://zero-to-jupyterhub.readthedocs.io/en/latest/security.html) for more details. It has seen a number of improvements, and we recommend +See the [security documentation](security) for more details. It has seen a number of improvements, and we recommend you read through it! ##### Autoscaling improvements @@ -2829,9 +2829,9 @@ which can add / remove nodes depending on how much your cluster is being used. In this release, we made a few changes to let z2jh interact better with the autoscaler! -- Configure z2jh to ['pack' your users](http://zero-to-jupyterhub.readthedocs.io/en/latest/advanced.html#picking-a-scheduler-strategy) +- Configure z2jh to ['pack' your users](optimization) onto nodes, rather than 'spread' them across nodes. -- A ['continuous' pre-puller](http://zero-to-jupyterhub.readthedocs.io/en/latest/advanced.html?highlight=prepull#pre-pulling-images-for-faster-startup) +- A ['continuous' pre-puller](pulling-images-before-users-arrive) that allows user images to be pulled on new nodes easily, leading to faster startup times for users on new nodes. ([link]) @@ -2854,7 +2854,7 @@ better supported by this version! Azure AKS is still in preview mode, so be aware of that before using it in any production workloads! -See the [setting up Kubernetes on Microsoft AKS](http://zero-to-jupyterhub.readthedocs.io/en/latest/create-k8s-cluster.html#setting-up-kubernetes-on-microsoft-azure-container-service-aks) section for more information. +See the [setting up Kubernetes on Microsoft AKS](microsoft-azure) section for more information. ##### Better configurability @@ -2863,13 +2863,13 @@ We now have better documentation and bug fixes for configurability! - `extraConfig` can be a dictionary instead of just a string. This helps when you have to split your `config.yaml` into multiple files for complex deployments -- How user storage works by default is [better documented](http://zero-to-jupyterhub.readthedocs.io/en/latest/user-storage.html) +- How user storage works by default is [better documented](user-storage) - Reading config in `extraConfig` from `extraConfigMap` now actually works! - You can configure the URL that users are directed to after they log in. - This allows [defaulting users to JupyterLab](http://zero-to-jupyterhub.readthedocs.io/en/latest/user-environment.html#use-jupyterlab-by-default) + This allows [defaulting users to JupyterLab](jupyterlab-by-default) - You can pre-pull multiple images now, for custom configuration that needs multiple images -- [Better instructions](http://zero-to-jupyterhub.readthedocs.io/en/latest/user-environment.html#pre-populating-user-s-home-directory-with-files) - on pre-populating your user's filesystem using [nbgitpuller](https://github.com/data-8/nbgitpuller) +- [Better instructions](use-nbgitpuller) + on pre-populating your user's filesystem using [nbgitpuller](https://github.com/jupyterhub/nbgitpuller) #### [Ellyse Perry](https://en.wikipedia.org/wiki/Ellyse_Perry) @@ -2902,7 +2902,7 @@ her take home the Player of the Match award. Perry featured prominently in Australia's three-peat of World T20 victories, selected for the Team of the Tournament in 2012 and 2014. -She was named [ICC Female Cricketer of the Year](http://www.abc.net.au/news/2017-12-22/ellyse-perry-named-iccs-womens-cricketer-of-the-year/9280538) in 2017. +She was named [ICC Female Cricketer of the Year](https://www.abc.net.au/news/2017-12-22/ellyse-perry-named-iccs-womens-cricketer-of-the-year/9280538) in 2017. #### Contributors @@ -2942,7 +2942,7 @@ In alphabetical order, ## 0.5 -### 0.5 - [Hamid Hassan](http://www.espncricinfo.com/afghanistan/content/player/311427.html) - 2017-12-05 +### 0.5 - [Hamid Hassan](https://www.espncricinfo.com/player/hamid-hassan-311427) - 2017-12-05 JupyterHub 0.8, HTTPS & scalability. @@ -2975,7 +2975,7 @@ And lots more! It is our responsibility as software authors to make it very easy for admins to set up HTTPS for their users. v0.5 makes this much easier than v0.4. You can find the new -instructions [here](http://zero-to-jupyterhub.readthedocs.io/en/latest/extending-jupyterhub.html#setting-up-https) and +instructions [here](https) and they are much simpler! You can also now use your own HTTPS certificates & keys rather than using Let's Encrypt. @@ -3001,7 +3001,7 @@ ways that's not yet possible with config.yaml. ##### Hub Services support -You can also add [external JupyterHub Services](http://jupyterhub.readthedocs.io/en/latest/reference/services.html) +You can also add [external JupyterHub Services](https://jupyterhub.readthedocs.io/en/latest/reference/services.html) by adding them to `hub.services`. Note that you are still responsible for actually running the service somewhere (perhaps as a deployment object). @@ -3018,10 +3018,10 @@ and `singleuser.fsGid`, mount extra volumes with `singleuser.storage.extraVolume Hamid Hassan is a fast bowler who currently plays for the Afghanistan National Cricket Team. With nicknames ranging from ["Afghanistan's David Beckham"](https://www.rferl.org/a/interview-afghan-cricketer-living-the-dream/24752618.html) to -["Rambo"](http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&objectid=11413633), +["Rambo"](https://www.nzherald.co.nz/nz/cricket-world-cup-rambo-ready-to-rumble/QAORUQEH6BHMOLRDABVXISQPPA/?c_id=1&objectid=11413633), he is considered by many to be Afghanistan's first Cricket Superhero. Currently known for fast (145km/h+) deliveries, cartwheeling celebrations, war painted -face and having had to flee Afghanistan as a child to escape from war. He [says](http://www.nzherald.co.nz/nz/news/article.cfm?c_id=1&objectid=11413633) +face and having had to flee Afghanistan as a child to escape from war. He [says](https://www.nzherald.co.nz/nz/cricket-world-cup-rambo-ready-to-rumble/QAORUQEH6BHMOLRDABVXISQPPA/?c_id=1&objectid=11413633) he plays because "We are ambassadors for our country and we want to show the world that Afghanistan is not like people recognise it by terrorists and these things. We want them to know that we have a lot of talent as well" @@ -3029,9 +3029,9 @@ things. We want them to know that we have a lot of talent as well" #### Contributors This release wouldn't have been possible without the wonderful contributors -to the [zero-to-jupyterhub](https://github.com/jupyterhub/zero-to-jupyterhub-k8s), +to the [zero-to-jupyterhub-k8s](https://github.com/jupyterhub/zero-to-jupyterhub-k8s), [JupyterHub](https://github.com/jupyterhub/jupyterhub), [KubeSpawner](https://github.com/jupyterhub/kubespawner) -and [OAuthenticator](http://github.com/jupyterhub/oauthenticator) repos. +and [OAuthenticator](https://github.com/jupyterhub/oauthenticator) repos. We'd like to thank everyone who contributed in any form - Issues, commenting on issues, PRs and reviews since the last Zero to JupyterHub release. @@ -3230,7 +3230,7 @@ installation. See [PR #56](https://github.com/jupyterhub/kubespawner/pull/56) on what needs to change. -- A **[StorageClass](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#storageclasses)** +- A **[StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/)** is no longer created by default. This shouldn't affect most new installs, since most cloud provider installations have a default (as of Kubernetes 1.6). If you are using an older version of Kubernetes, the easiest thing to do is to @@ -3266,13 +3266,13 @@ installation. #### Changed -- We now use the official [configurable http proxy](http://github.com/jupyterhub/configurable-http-proxy) +- We now use the official [configurable http proxy](https://github.com/jupyterhub/configurable-http-proxy) (CHP) as the proxy, rather than the unofficial [nchp](https://github.com/yuvipanda/jupyterhub-nginx-chp). This should be a no-op (or require no changes) for the most part. JupyterHub errors might display a nicer error page. - The version of KubeSpawner uses the official Kubernetes - [python client](https://github.com/kubernetes-incubator/client-python/) rather + [python client](https://github.com/kubernetes-client/python) rather than [pycurl](http://pycurl.io/). This helps with scalability a little. #### Removed diff --git a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md index f578679a31..0e0e9902f1 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md @@ -9,7 +9,7 @@ These instructions involve parts of the Azure command line that are in preview, You can create a Kubernetes cluster [either through the Azure portal website, or using the Azure command line tools](https://docs.microsoft.com/en-us/azure/aks/). This page describes the commands required to setup a Kubernetes cluster using the command line. -If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal). +If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal). 1. Prepare your Azure shell environment. You have two options, one is to use the Azure interactive shell, the other is to install the Azure command-line @@ -157,7 +157,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta To enable this in Azure, we must first create a [Virtual Network](https://docs.microsoft.com/en-gb/azure/virtual-network/virtual-networks-overview) with Azure's own network policies enabled. - This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://docs.projectcalico.org) network policies. + This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://projectcalico.docs.tigera.io/about/about-calico) network policies. ``` az network vnet create \ diff --git a/docs/source/kubernetes/redhat/step-zero-openshift.md b/docs/source/kubernetes/redhat/step-zero-openshift.md index c93fbc15cb..816d8f7c8b 100644 --- a/docs/source/kubernetes/redhat/step-zero-openshift.md +++ b/docs/source/kubernetes/redhat/step-zero-openshift.md @@ -7,10 +7,10 @@ For setting up JupyterHub on OpenShift, check out the [JupyterHub on OpenShift](https://github.com/jupyter-on-openshift/jupyterhub-quickstart) project. It provides an OpenShift template based JupyterHub deployment. Zero to JupyterHub uses [helm](https://helm.sh) which is currently usable with OpenShift; yet deploying helm on OpenShift -is somewhat complicated (see RedHat's blog post on [Getting Started with Helm on OpenShift](https://www.openshift.com/blog/getting-started-helm-openshift)). +is somewhat complicated (see RedHat's blog post on [Getting Started with Helm on OpenShift](https://cloud.redhat.com/blog/getting-started-helm-openshift)). ## Additional resources about Jupyter on OpenShift - An excellent series of OpenShift blog posts on Jupyter and OpenShift authored by Red Hat developer, Graham Dumpleton, are - available on the [OpenShift blog](https://www.openshift.com/blog/jupyter-openshift-using-openshift-data-analytics). + available on the [OpenShift blog](https://cloud.redhat.com/blog/jupyter-openshift-using-openshift-data-analytics). diff --git a/images/image-awaiter/README.md b/images/image-awaiter/README.md index 96f088e5d6..891cd7fb8f 100644 --- a/images/image-awaiter/README.md +++ b/images/image-awaiter/README.md @@ -16,7 +16,7 @@ minutes to a few seconds. ### What technical knowledge is needed to understand this? -You need to know about [Kubernetes Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/) and [Kubernetes DaemonSets](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/), about [Helm and helm hooks](https://github.com/kubernetes/helm/blob/HEAD/docs/charts_hooks.md), +You need to know about [Kubernetes Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/job/) and [Kubernetes DaemonSets](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/), about [Helm and helm hooks](https://helm.sh/docs/topics/charts_hooks/), and about the programming language Go. ### Why is this project in Go? Isn't the Jupyter Infrastructure ecosystem mostly Python? diff --git a/images/singleuser-sample/README.md b/images/singleuser-sample/README.md index f5e59994d7..ce87c71412 100644 --- a/images/singleuser-sample/README.md +++ b/images/singleuser-sample/README.md @@ -29,4 +29,4 @@ This image available tags can be found [here](https://hub.docker.com/r/jupyterhu - Ubuntu Linux - v18.04 aka. Bionic - JupyterHub - required by with Helm chart since KubeSpawner requires it -- [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) and [JupyterLab-Hub extension](https://jupyterlab.readthedocs.io/en/stable/user/jupyterhub.html) - to activate it over the classical UI by default, see [the guide's instructions](https://z2jh.jupyter.org/en/latest/user-environment.html#use-jupyterlab-by-default). +- [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/) and [JupyterLab-Hub extension](https://jupyterlab.readthedocs.io/en/stable/user/jupyterhub.html) - to activate it over the classical UI by default, see [the guide's instructions](https://z2jh.jupyter.org/en/latest/jupyterhub/customizing/user-environment.html#use-jupyterlab-by-default). From 30aebe20768aed1b492829f3486b23e31e6b5dae Mon Sep 17 00:00:00 2001 From: Ruben Rodriguez Date: Fri, 8 Jul 2022 13:18:58 +0200 Subject: [PATCH 417/898] Update values & schema --- jupyterhub/schema.yaml | 27 +++++++++++++++++++++++++++ jupyterhub/values.yaml | 3 +++ 2 files changed, 30 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index bd2d082e8d..14147193eb 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2356,6 +2356,15 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + labels: + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Extra labels to add to the userScheduler pods. + + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) + to learn more about labels. annotations: type: object additionalProperties: false @@ -2506,6 +2515,15 @@ properties: type: integer description: | How many placeholder pods would you like to have? + labels: + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Extra labels to add to the userPlaceholder pods. + + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) + to learn more about labels. annotations: type: object additionalProperties: false @@ -2657,6 +2675,15 @@ properties: additionalProperties: false required: [hook, continuous] properties: + labels: + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Extra labels to add to the pre puller job pods. + + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) + to learn more about labels. annotations: type: object additionalProperties: false diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index fa9d015462..83fe7994ec 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -506,6 +506,7 @@ scheduling: pullSecrets: [] nodeSelector: {} tolerations: [] + labels: {} annotations: {} pdb: enabled: true @@ -536,6 +537,7 @@ scheduling: pullPolicy: pullSecrets: [] replicas: 0 + labels: {} annotations: {} containerSecurityContext: runAsUser: 65534 # nobody user @@ -569,6 +571,7 @@ scheduling: # prePuller relates to the hook|continuous-image-puller DaemonsSets prePuller: + labels: {} annotations: {} resources: {} containerSecurityContext: From 2aa99e9e05f811d767c5bfe593075c2addc469a4 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 11 Jul 2022 05:24:46 +0000 Subject: [PATCH 418/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 53ac003863..976ae04aa8 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -2,7 +2,7 @@ # --------------- FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-06-27_06:14:10 +# VULN_SCAN_TIME=2022-07-11_05:24:45 WORKDIR /build-stage From 274f6a13c55b09fd9e2b49cd234379d97109bf8b Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 11 Jul 2022 15:10:55 +0000 Subject: [PATCH 419/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 41fb972ffc..3320eb09e4 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -28,7 +28,7 @@ certifi==2022.6.15 # requests certipy==0.1.3 # via jupyterhub -cffi==1.15.0 +cffi==1.15.1 # via # bcrypt # cryptography @@ -36,7 +36,7 @@ charset-normalizer==2.1.0 # via # aiohttp # requests -cryptography==37.0.2 +cryptography==37.0.4 # via pyopenssl entrypoints==0.4 # via jupyterhub @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.6.1 +jsonschema==4.7.1 # via # jupyter-telemetry # oauthenticator @@ -94,7 +94,7 @@ kubernetes-asyncio==23.6.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.2.0 +mako==1.2.1 # via alembic markupsafe==2.1.1 # via @@ -151,7 +151,7 @@ python-dateutil==2.8.2 # jupyterhub # jupyterhub-idle-culler # kubernetes-asyncio -python-json-logger==2.0.2 +python-json-logger==2.0.4 # via jupyter-telemetry python-slugify==6.1.2 # via jupyterhub-kubespawner @@ -190,7 +190,7 @@ statsd==3.3.0 # via -r requirements.in text-unidecode==1.3 # via python-slugify -tornado==6.1 +tornado==6.2 # via # jupyterhub # jupyterhub-idle-culler @@ -200,7 +200,7 @@ traitlets==5.3.0 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.9 +urllib3==1.26.10 # via # jupyterhub-kubespawner # kubernetes-asyncio From c48f5e5a830267a837d619fa9ccc3d6c459d27a6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 21:55:11 +0000 Subject: [PATCH 420/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.34.0 → v2.37.1](https://github.com/asottile/pyupgrade/compare/v2.34.0...v2.37.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9b2ba962b1..1e7c0ecc51 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.34.0 + rev: v2.37.1 hooks: - id: pyupgrade args: From 43db2e671fe0690000d5680f22ba99df4accf3aa Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 12 Jul 2022 05:34:08 +0000 Subject: [PATCH 421/898] Update library/traefik version from v2.8.0 to v2.8.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 83fe7994ec..93a3f6ff81 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -246,7 +246,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.0" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.1" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 0acbcf4332e9ce3770e9df888e154fbb95e05084 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 12 Jul 2022 06:15:34 +0000 Subject: [PATCH 422/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3320eb09e4..b2a0fcf534 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.7.1 +jsonschema==4.7.2 # via # jupyter-telemetry # oauthenticator From 85c548f724d9eb17c609c179d76fc5f99ced4644 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Jul 2022 13:22:53 +0200 Subject: [PATCH 423/898] ci: workaround k3s issue until resolved upstream --- .github/workflows/test-chart.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 437316f83b..9ce8faac9d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -127,10 +127,15 @@ jobs: # # k3s-version: https://github.com/rancher/k3s/tags # k3s-channel: https://update.k3s.io/v1-release/channels + # + # NOTE: we workaround a bug for k3s 1.22+ by passing `extra-setup-args: --egress-selector-mode=disabled` + # include: - k3s-channel: latest + k3s-extra-setup-args: --egress-selector-mode=disabled test: install - k3s-channel: stable + k3s-extra-setup-args: --egress-selector-mode=disabled test: install - k3s-channel: v1.21 # also test prePuller.hook test: install @@ -161,6 +166,7 @@ jobs: # https://jupyterhub.github.io/helm-chart/info.json # - k3s-channel: v1.23 + k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade upgrade-from: stable upgrade-from-extra-args: >- @@ -174,6 +180,7 @@ jobs: --set singleuser.storage.type=dynamic create-k8s-test-resources: true - k3s-channel: v1.23 + k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade upgrade-from: dev upgrade-from-extra-args: >- @@ -184,6 +191,7 @@ jobs: --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic - k3s-channel: v1.24 + k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade # We're testing hub.db.upgrade with PostgreSQL so this version must be old # enough to require a DB upgrade @@ -229,6 +237,7 @@ jobs: metrics-enabled: false traefik-enabled: false docker-enabled: true + extra-setup-args: "${{ matrix.k3s-extra-setup-args }}" - uses: actions/setup-python@v4 with: From 030686a50b671f04c13f600b86c6885a4c4568e3 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 12 Jul 2022 13:23:11 +0200 Subject: [PATCH 424/898] ci: ensure we have a test against k8s 1.22 --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 9ce8faac9d..d9525b5833 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -190,7 +190,7 @@ jobs: local-chart-extra-args: >- --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic - - k3s-channel: v1.24 + - k3s-channel: v1.22 k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade # We're testing hub.db.upgrade with PostgreSQL so this version must be old From e9c8d9194f3cbe57614ac830f55dbbf163670f7e Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 14 Jul 2022 05:26:03 +0000 Subject: [PATCH 425/898] Update kube-scheduler version from v1.23.8 to v1.23.9 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 93a3f6ff81..d81ba3d683 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -501,7 +501,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.8" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.9" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From d01f3d63c479dc4227ae50d403a8fc5ba091d67c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Dec 2021 15:03:00 +0100 Subject: [PATCH 426/898] docs: add changelog for 2.0.0 Iterative improvements on the changelog 2.0.0 entry Co-authored-by: Simon Li docs: use sphinx references instead of hardcoded links docs: small changelog updates [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci docs: added descriptions of breaking changes Co-authored-by: Simon Li [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Port fixes from changelog to new upgrade guide Remove breaking/upgrade guide from changelog, link to new guide Add 2.0.0 notable deps anchor to changelog Co-authored-by: Min RK [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Include example of args in singleuser.cmd Add description of changes to changelog Co-authored-by: Simon Li 2.0.0-beta.1, with warnings Update 2.0.0 changelog with more PRs Update 2.0.0 dependencies table Remove some backticks which somehow crept in --- .../administrator/upgrading/upgrade-1-to-2.md | 34 ++- docs/source/changelog.md | 220 ++++++++++++++++++ 2 files changed, 246 insertions(+), 8 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 92957cf657..914d68e1f9 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -3,6 +3,8 @@ Z2JH 2 contains several breaking changes, including some that affect the security of your deployment. This guide will help you upgrade from 1.\* to 2.\*. +(upgrade-1-2-security-breaking-change)= + ## Security: breaking change to `*.networkPolicy.egress` NetworkPolicy egress rules have been extended with a new property. @@ -49,18 +51,24 @@ and the configuration reference entries under Z2JH 2.0.0 upgrades JupyterHub to the 2.\* series, and also upgrades all hub components. If you are using any custom JupyterHub services, addons, API integrations, or extra configuration, you should review the breaking changes in the -[JupyterHub 2.0.0 changelog](https://github.com/jupyterhub/jupyterhub/blob/2.3.1/docs/source/changelog.md#200). +[JupyterHub 2.x changelog](https://jupyterhub.readthedocs.io/en/2.3.1/changelog.html). -JupyterHub 2 uses an updated database schema. -Z2JH 2.0.0 automatically handles the upgrade for SQLite databases (the default), but if you use an external database you need to configure [`hub.db.upgrade`](schema_hub.db.upgrade) to true when upgrading. It will not be possible to downgrade to older releases after this without also using a backup or resetting the database. +JupyterHub 2 updates the database schema, which means a migration takes place when you upgrade JupyterHub. +Z2JH automatically handles the upgrade if you are using sqlite (`hub.db.type = 'sqlite-pvc'`, the default), but it may not be possible to downgrade to older releases after this. +When using sqlite, JupyterHub automatically creates a backup in the `hub-db` volume, +which can be restored manually if you need to downgrade. +If you use an external database you need to configure [`hub.db.upgrade`](schema_hub.db.upgrade) to `true` when upgrading. JupyterHub 2 adds RBAC for managing permissions in JupyterHub. -The old permissions model of admin/non-admin still works but you should use -[RBAC to assign the required privileges to users or services in future](https://jupyterhub.readthedocs.io/en/stable/rbac/index.html) +The old permissions model of admin/non-admin still works, but we recommend using [RBAC to assign only the required privileges to users or services in future](https://jupyterhub.readthedocs.io/en/stable/rbac/index.html). +Default permissions are mostly unchanged, but a few have: + +- Servers' own API tokens have limited permissions by default, which can be expanded by defining the `server` role. The previous behavior was the maximum permission of `inherit`. +- `admin_access` as a concept is removed, so disabling it has no effect. In 2.0, admins by definition can do everything, including access servers. To limit user permissions, assign them to roles which have only the needed permissions. See -`TODO: link to Notable dependencies updated` -in the changelog for more information on other upgraded hub components. +[Notable dependencies updated](notable-dependencies-200) +for more information on other upgraded hub components. ## JupyterLab and Jupyter Server @@ -69,7 +77,7 @@ To switch back to Jupyter Notebook either configure/rebuild your singleuser imag ## Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) -Z2JH now launches the container's default command (equivalent to setting `CMD` in a `Dockerfile`) instead of overriding it. +Z2JH now launches the container's default command (as set e.g. by `CMD` in a `Dockerfile`) instead of overriding it. This ensures that containers that use a custom start command to configure their environment, such as some [Jupyter Docker Stacks](https://jupyter-docker-stacks.readthedocs.io/en/latest/) images, will work without any changes. @@ -80,6 +88,16 @@ singleuser: cmd: jupyterhub-singleuser ``` +If you want to add custom arguments to the command, you must specify the full command and any arguments in `singleuser.cmd`, for example: + +```yaml +singleuser: + cmd: + - jupyterhub-singleuser + - "--collaborative" + - "--debug" +``` + ## Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) Previously if `hub.config` was used to configure some JupyterHub traitlets it would override any custom configuration files mounted into `jupyterhub_config.d` in the hub container. diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 938de3dcc6..8471d95da3 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,6 +12,226 @@ and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. +## 2.0 + +### 2.0.0-beta.1 + +WARNING: This is a beta release of the JupyterHub Helm chart. + +We do not expect to make any significant changes between this and the final 2.0.0 release unless major bugs are found. +Ensure you backup your system and test any upgrades if you plan to use this in production. + +#### Highlights + +Z2JH 2.0.0 is the first major release since 1.0.0 was released in June 2021, and contains major upgrades to all JupyterHub components. + +JupyterHub 2.x includes new RBAC support allowing fine-grained access control to hub services and servers. + +JupyterLab, the next-generation Notebook interface, is now the default interface seen by users. This brings a full development environment with a large number of extensions developed by the Jupyter community. + +This release also includes several smaller changes that help Z2JH interface better with the rest of the Jupyter community such as not overriding a Docker image's command, and using standard Helm chart parameter names to match with other Helm charts. +Although these are breaking changes they will greatly improve the maintainability of the JupyterHub chart in future, and should also make it easier for new users to get started. + +#### Security: breaking change to `*.networkPolicy.egress` + +If you have configured any of: + +- `hub.networkPolicy.egress` +- `proxy.chp.networkPolicy.egress` +- `proxy.traefik.networkPolicy.egress` +- `singleuser.networkPolicy.egress` + +you must review your configuration as additional default egress routes have been added. +See [](upgrade-1-2-security-breaking-change) for details. + +#### Upgrade instructions + +Please read through all breaking changes, then follow the [upgrading guide](administrator/upgrading/index). + +#### Breaking changes + +These breaking changes have been made relative to the 1.\* series of Z2JH releases: + +- Security: breaking change to `*.networkPolicy.egress` +- JupyterHub upgraded to 2.x along with related hub components +- JupyterLab and Jupyter Server is now the default singleuser application +- Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) +- Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) +- User scheduler plugin configuration has changed to match `kubescheduler.config.k8s.io/v1beta3` [#2590](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2590) +- Kubernetes version 1.20+ is required [#2635](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2635) +- `hub.fsGid` is replaced by `hub.podSecurityContext` [#2720](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2720) +- Hub image is based on Debian instead of Ubuntu [#2733](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2733) +- Disabling RBAC requires setting multiple properties, `rbac.enable` is removed [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) + +For information on how to update your configuration see the [](administrator/upgrading/upgrade-1-to-2) guide. + +#### TODO: link out to relevant informational posts + +TODO: Write a discourse forum post about the following and highlight that via +the changelog. + +- 100+ user-placeholder pods -> node-placeholder pods +- autohttps acme acquisition issues on GKE mitigation + (notable-dependencies-200)= + +#### Notable dependencies updated + +| Dependency | Version in 1.2.0 | Version in 2.0.0 | Changelog link | Note | +| -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 1.4.2 | 2.3.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | +| [kubespawner](https://github.com/jupyterhub/kubespawner) | 1.1.0 | 4.1.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 14.2.0 | 15.0.1 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | +| [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.0.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 0.0.7 | 1.0.5 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | +| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.0 | 4.5.1 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | +| [traefik](https://github.com/traefik/traefik) | v2.4.11 | v2.8.1 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.19.13 | v1.23.9 | - | Run in the `user-scheduler` pod(s) | + +For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt) file and use its git history to see what changes between tagged versions. + +#### New features added + +- Add scheduling.userScheduler.annotations [#2763](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2763) ([@joncotton](https://github.com/joncotton)) +- Add scheduling.userPlaceholder.annotations [#2762](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2762) ([@joncotton](https://github.com/joncotton)) +- Add `.create` and `.name` to serviceAccount config, and decouple `rbac.enable` from the service accounts [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) ([@dingobar](https://github.com/dingobar), [@consideRatio](https://github.com/consideRatio), [@desaintmartin](https://github.com/desaintmartin)) +- breaking: add hub.podSecurityContext, remove hub.fsGid [#2720](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2720) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Add singleuser.allowPrivilegeEscalation for KubeSpawner 2+ [#2713](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2713) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Add `proxy.traefik.extraInitContainers` config [#2670](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2670) ([@gregingenii](https://github.com/gregingenii), [@yuvipanda](https://github.com/yuvipanda)) +- Support idle culler --cull-admin-users [#2578](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2578) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- Unset `singleuser.cmd`, previously `jupyterhub-singleuser`, to instead rely on the image's CMD by default [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics), [@meeseeksmachine](https://github.com/meeseeksmachine)) + +#### Enhancements made + +- Add `labels` config for `scheduling.userScheduler`, `scheduling.userPlaceholder`, and `prePuller` [#2791](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2791) ([@ruben-rodriguez](https://github.com/ruben-rodriguez)) +- Enable parent chart's (binderhub etc) to use imagePullSecrets helper [#2546](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2546) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- Add hub.loadRoles configuration [#2405](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2405) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- Add ingress.ingressClassName config option [#2403](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2403) ([@consideRatio](https://github.com/consideRatio)) + +#### Bugs fixed + +- Fix for PDBs in k8s 1.20 [#2727](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2727) ([@consideRatio](https://github.com/consideRatio), [@geoffo-dev](https://github.com/geoffo-dev)) +- Enable image-puller pods to evict user-placeholder pods [#2681](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2681) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda), [@a3626a](https://github.com/a3626a)) +- Fix failure to respect proxy.secretSync.resources configuration [#2628](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2628) ([@jhowton-restor3d](https://github.com/jhowton-restor3d), [@consideRatio](https://github.com/consideRatio)) +- Remove typo " in schema.yaml [#2603](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2603) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- match config load priority for jupyterhub_config.d files and hub.extraConfig [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio), [@MLobo1997](https://github.com/MLobo1997)) +- idle-culler: fix the new restricted scopes to include read:servers [#2446](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2446) ([@consideRatio](https://github.com/consideRatio), [@snickell](https://github.com/snickell)) +- Add config singleuser.networkTools.resources - all containers must have configurable resources [#2439](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2439) ([@consideRatio](https://github.com/consideRatio)) +- Fix implementation of restricted scopes for jupyterhub-idle-culler [#2434](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2434) ([@consideRatio](https://github.com/consideRatio)) +- Fix proxy pod's liveness/readiness probes to be fully configurable [#2421](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2421) ([@consideRatio](https://github.com/consideRatio), [@mriedem](https://github.com/mriedem)) + +#### Maintenance and upkeep improvements + +- Adjust kerning on large JupyterHub in NOTES.txt [#2787](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2787) ([@manics](https://github.com/manics)) +- 2.0.0 upgrade guide [#2779](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2779) ([@manics](https://github.com/manics)) +- hub image: remove wheel building aarch64 workaround for pycryptodomex [#2766](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2766) ([@consideRatio](https://github.com/consideRatio)) +- hub image: downgrade to ltiauthenticator 1.2.0 [#2741](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2741) ([@consideRatio](https://github.com/consideRatio)) +- breaking, maint: replace rbac.enabled with rbac.create [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- breaking: hub image ubuntu->debian, py38->py39, `build-essential` removed, `--build-arg PIP_OVERRIDES=...` removed, images/hub/dependencies removed [#2733](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2733) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk)) +- maint: update import statement for py310 compatibility [#2732](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2732) ([@consideRatio](https://github.com/consideRatio)) +- maint: add pre-commit isort hook, and let pyupgrade assume py38+ in hub container [#2730](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2730) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Bump versions of image-awaiter dependencies [#2725](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2725) ([@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio)) +- maint: cleanup deprecation warning introduced in 0.10.0 (assume users upgrade to v2 from v1) [#2719](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2719) ([@consideRatio](https://github.com/consideRatio)) +- Update pause version from 3.6 to 3.7 [#2700](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2700) ([@github-actions](https://github.com/github-actions), [@consideRatio](https://github.com/consideRatio)) +- Update kube-scheduler version from v1.23.4 to v1.23.6 [#2699](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2699) ([@github-actions](https://github.com/github-actions), [@consideRatio](https://github.com/consideRatio)) +- Update library/traefik version to v2.6.6 [#2695](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2695) ([@github-actions](https://github.com/github-actions), [@consideRatio](https://github.com/consideRatio)) +- Require k8s 1.20+ and small cleanups based on assuming it [#2635](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2635) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Use chart logo from https://jupyterhub.github.io/helm-chart [#2604](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2604) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- Update user-scheduler's kube-scheduler binary and config when in k8s clusters versioned >=1.21 [#2590](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2590) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- image-awaiter: fix known vulns. by updating to golang:1.17 in image build stage [#2562](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2562) ([@nreith](https://github.com/nreith), [@consideRatio](https://github.com/consideRatio)) +- Improved nodeSelector formatting in template [#2554](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2554) ([@ostapkonst](https://github.com/ostapkonst), [@consideRatio](https://github.com/consideRatio)) +- Remove workaround to have PriorityClass resources as helm hooks [#2526](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2526) ([@consideRatio](https://github.com/consideRatio)) +- deps: update traefik, kube-scheduler, and pause image [#2524](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2524) ([@consideRatio](https://github.com/consideRatio)) +- refactor: move doc to docs, use \_build instead of build [#2521](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2521) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- breaking: add ...networkPolicy.egressAllowRules and don't allow singleuser pods to access PrivateIPv4 addresses by default [#2508](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2508) ([@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics), [@minrk](https://github.com/minrk), [@choldgraf](https://github.com/choldgraf)) +- Update with changes introduced in 1.1.4 security patch [#2459](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2459) ([@consideRatio](https://github.com/consideRatio), [@minrk](https://github.com/minrk)) +- Add missing default values for proxy pod's probes [#2423](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2423) ([@consideRatio](https://github.com/consideRatio), [@mriedem](https://github.com/mriedem)) +- Update NOTES.txt [#2411](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2411) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Use f-strings instead of the % pattern [#2408](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2408) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- Remove breaking change messages relevant for upgrading to 1.0.0 [#2397](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2397) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) +- Pin jupyterhub==2.0.0b1 and refreeze dependencies [#2396](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2396) ([@consideRatio](https://github.com/consideRatio)) +- Tighten permissions for jupyterhub-idle-culler [#2395](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2395) ([@consideRatio](https://github.com/consideRatio), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics)) +- pre-commit: add and run pyupgrade [#2394](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2394) ([@consideRatio](https://github.com/consideRatio)) + +#### Documentation improvements + +- docs: fix most broken links in changelog [#2790](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2790) ([@consideRatio](https://github.com/consideRatio)) +- Fix `redirected permanently` linkcheck apart from changelog [#2789](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2789) ([@manics](https://github.com/manics)) +- docs: remove broken links and use https over http in a few [#2775](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2775) ([@consideRatio](https://github.com/consideRatio)) +- docs: transition rST based glossary to MyST [#2770](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2770) ([@consideRatio](https://github.com/consideRatio)) +- docs: move changelog from pure markdown to sphinx based docs [#2769](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2769) ([@consideRatio](https://github.com/consideRatio)) +- docs: update notes about building wheels in build stage [#2742](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2742) ([@consideRatio](https://github.com/consideRatio)) +- Fix link to authentication guide when viewed via GitHub's UI [#2740](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2740) ([@jdmcbr](https://github.com/jdmcbr), [@consideRatio](https://github.com/consideRatio)) +- Add opengraph tags [#2717](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2717) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio), [@choldgraf](https://github.com/choldgraf)) +- docs: how to adjust profile_list dynamically based on user etc [#2697](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2697) ([@consideRatio](https://github.com/consideRatio), [@choldgraf](https://github.com/choldgraf)) +- `proxy.service.type`: link directly to k8s docs [#2672](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2672) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- docs: Remove a false promise regarding CHOWN_HOME [#2640](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2640) ([@dmigo](https://github.com/dmigo), [@yuvipanda](https://github.com/yuvipanda)) +- update step-zero-microk8s.md [#2630](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2630) ([@theomper](https://github.com/theomper), [@minrk](https://github.com/minrk)) +- Fix broken internal references in docs [#2600](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2600) ([@consideRatio](https://github.com/consideRatio)) +- docs: fix syntax error in note directive [#2568](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2568) ([@sunu](https://github.com/sunu), [@consideRatio](https://github.com/consideRatio)) +- Corrected datascience to Data Science. [#2560](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2560) ([@Adam-Antios](https://github.com/Adam-Antios), [@consideRatio](https://github.com/consideRatio)) +- docs: fix broken anchor in link [#2547](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2547) ([@consideRatio](https://github.com/consideRatio)) +- Use chart logo from this repo [#2544](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2544) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- Replace zone to allowedTopologies [#2543](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2543) ([@kindomLee](https://github.com/kindomLee), [@consideRatio](https://github.com/consideRatio)) +- DOC: Expand IAM abbreviation for comprehensibility [#2535](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2535) ([@raybellwaves](https://github.com/raybellwaves), [@consideRatio](https://github.com/consideRatio)) +- Don't pin example jupyter/minimal-notebook [#2522](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2522) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- Link to Discourse instead of the mailing list [#2519](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2519) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- DOC: add extra AWS ssh info [#2518](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2518) ([@raybellwaves](https://github.com/raybellwaves), [@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- docs: document `singleuser.someConfig` by linking to `KubeSpawner.some_config` docs [#2517](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2517) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- Document how to disable some labextensions with config [#2516](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2516) ([@yuvipanda](https://github.com/yuvipanda), [@consideRatio](https://github.com/consideRatio)) +- DOC: instructions for role creation in AWS [#2514](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2514) ([@raybellwaves](https://github.com/raybellwaves), [@consideRatio](https://github.com/consideRatio)) +- Update MetalLB section in step-zero-microk8s.md [#2511](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2511) ([@wyphan](https://github.com/wyphan), [@consideRatio](https://github.com/consideRatio)) +- Fix docs typo [#2489](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2489) ([@mriedem](https://github.com/mriedem), [@consideRatio](https://github.com/consideRatio)) +- Add changelog for 1.2.0 [#2480](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2480) ([@consideRatio](https://github.com/consideRatio)) +- docs: fix syntax errors with directives [#2478](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2478) ([@consideRatio](https://github.com/consideRatio)) +- docs: fix failure to show correct version [#2474](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2474) ([@consideRatio](https://github.com/consideRatio)) +- Fix indentation in local-storage-dir.yaml [#2443](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2443) ([@timotk](https://github.com/timotk), [@consideRatio](https://github.com/consideRatio)) +- auth rework: update forgotten documentation [#2438](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2438) ([@abdelq](https://github.com/abdelq), [@consideRatio](https://github.com/consideRatio)) +- Minor fixes to the Microk8s documentation [#2436](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2436) ([@mike-matera](https://github.com/mike-matera), [@consideRatio](https://github.com/consideRatio)) +- Fixes broken link in chart docs [#2432](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2432) ([@joraff](https://github.com/joraff), [@consideRatio](https://github.com/consideRatio)) +- Retrospectively add breaking change to changelog entry 0.10.0 [#2410](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2410) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- Update links that redirected [#2409](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2409) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- example configurations for UI choices [#2398](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2398) ([@minrk](https://github.com/minrk), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics), [@willingc](https://github.com/willingc)) +- Add changelog for 1.1.3 [#2361](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2361) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- Documenting Microk8s cluster type. [#2334](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2334) ([@mike-matera](https://github.com/mike-matera), [@consideRatio](https://github.com/consideRatio)) + +#### Continuous integration improvements + +- ci: workaround intermittent test failures pending upstream fix in k3s [#2800](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2800) ([@consideRatio](https://github.com/consideRatio)) +- Test postgres schema upgrade in CI [#2785](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2785) ([@manics](https://github.com/manics)) +- ci: fix permissions for vuln scan workflow [#2754](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2754) ([@consideRatio](https://github.com/consideRatio)) +- ci: revert mistakenly added temp debugging change [#2753](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2753) ([@consideRatio](https://github.com/consideRatio)) +- ci: add a refreeze requirements.txt job and use dedicated gha env [#2748](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2748) ([@consideRatio](https://github.com/consideRatio)) +- ci: reduce frequency of gha/vuln bumps [#2729](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2729) ([@consideRatio](https://github.com/consideRatio)) +- ci: don't trigger 2x tests on bump automation PRs [#2711](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2711) ([@consideRatio](https://github.com/consideRatio)) +- ci: use jupyterhub-bot PAT to trigger github workflow on opened PRs [#2709](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2709) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- ci: fix syntax error in dependabot config [#2707](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2707) ([@consideRatio](https://github.com/consideRatio)) +- ci: followup tweaks to dependency bumping automation [#2703](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2703) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- ci: automatically bump kube-scheduler and pause image tags [#2698](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2698) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- ci: add automation to bump jupyterhub version and refreeze deps while doing it [#2696](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2696) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- ci: add automation to update chp and traefik images [#2694](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2694) ([@consideRatio](https://github.com/consideRatio), [@sgibson91](https://github.com/sgibson91)) +- ci: add support bot [#2618](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2618) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio), [@minrk](https://github.com/minrk)) +- ci: remove conditional tmate debugging session action [#2584](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2584) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- ci: test against k8s 1.23 [#2548](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2548) ([@consideRatio](https://github.com/consideRatio)) +- ci: remove workaround installing six [#2527](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2527) ([@consideRatio](https://github.com/consideRatio)) +- ci: vuln-scan, adjust to changes in trivy's json output [#2463](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2463) ([@consideRatio](https://github.com/consideRatio)) +- ci: don't re-install yq - its already available [#2440](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2440) ([@consideRatio](https://github.com/consideRatio)) +- ci: don't run twice for pre-commit PRs [#2425](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2425) ([@consideRatio](https://github.com/consideRatio)) +- ci: update shellcheck [#2420](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2420) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- ci: refresh circleci config [#2418](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2418) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) +- ci: test against k8s 1.22 [#2404](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2404) ([@consideRatio](https://github.com/consideRatio)) +- ci: use PVCs when testing upgrades [#2401](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2401) ([@manics](https://github.com/manics), [@consideRatio](https://github.com/consideRatio)) +- ci: remove no longer needed arm test adjustment [#2376](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2376) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) + +#### Contributors to this release + +## Contributors to this release + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2021-08-25&to=2022-07-14&type=c)) + +[@abdelq](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aabdelq+updated%3A2021-08-25..2022-07-14&type=Issues) | [@Adam-Antios](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAdam-Antios+updated%3A2021-08-25..2022-07-14&type=Issues) | [@alex-g-tejada](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalex-g-tejada+updated%3A2021-08-25..2022-07-14&type=Issues) | [@AlexChung1995](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAlexChung1995+updated%3A2021-08-25..2022-07-14&type=Issues) | [@BertR](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ABertR+updated%3A2021-08-25..2022-07-14&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2021-08-25..2022-07-14&type=Issues) | [@bjornarfjelldal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornarfjelldal+updated%3A2021-08-25..2022-07-14&type=Issues) | [@chancez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achancez+updated%3A2021-08-25..2022-07-14&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2021-08-25..2022-07-14&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-08-25..2022-07-14&type=Issues) | [@cslovell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acslovell+updated%3A2021-08-25..2022-07-14&type=Issues) | [@delamart](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adelamart+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dhirschfeld](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adhirschfeld+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dingobar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adingobar+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dmigo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Admigo+updated%3A2021-08-25..2022-07-14&type=Issues) | [@Economax](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AEconomax+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ellisonbg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aellisonbg+updated%3A2021-08-25..2022-07-14&type=Issues) | [@GeorgianaElena](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGeorgianaElena+updated%3A2021-08-25..2022-07-14&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2021-08-25..2022-07-14&type=Issues) | [@gregingenii](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agregingenii+updated%3A2021-08-25..2022-07-14&type=Issues) | [@gsemet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agsemet+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jdmcbr](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajdmcbr+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jhowton-restor3d](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajhowton-restor3d+updated%3A2021-08-25..2022-07-14&type=Issues) | [@joncotton](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoncotton+updated%3A2021-08-25..2022-07-14&type=Issues) | [@joraff](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoraff+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jupyterhub-bot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2021-08-25..2022-07-14&type=Issues) | [@kindomLee](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AkindomLee+updated%3A2021-08-25..2022-07-14&type=Issues) | [@lud0v1c](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Alud0v1c+updated%3A2021-08-25..2022-07-14&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-08-25..2022-07-14&type=Issues) | [@matthew-brett](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amatthew-brett+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mcberma](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amcberma+updated%3A2021-08-25..2022-07-14&type=Issues) | [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ameeseeksmachine+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mike-matera](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amike-matera+updated%3A2021-08-25..2022-07-14&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-08-25..2022-07-14&type=Issues) | [@MLobo1997](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AMLobo1997+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mriedem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amriedem+updated%3A2021-08-25..2022-07-14&type=Issues) | [@nreith](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Anreith+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ostapkonst](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aostapkonst+updated%3A2021-08-25..2022-07-14&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apre-commit-ci+updated%3A2021-08-25..2022-07-14&type=Issues) | [@pvanliefland](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apvanliefland+updated%3A2021-08-25..2022-07-14&type=Issues) | [@raybellwaves](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Araybellwaves+updated%3A2021-08-25..2022-07-14&type=Issues) | [@remche](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aremche+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ruben-rodriguez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aruben-rodriguez+updated%3A2021-08-25..2022-07-14&type=Issues) | [@sgibson91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgibson91+updated%3A2021-08-25..2022-07-14&type=Issues) | [@snickell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asnickell+updated%3A2021-08-25..2022-07-14&type=Issues) | [@srggrs](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asrggrs+updated%3A2021-08-25..2022-07-14&type=Issues) | [@sunu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asunu+updated%3A2021-08-25..2022-07-14&type=Issues) | [@support](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asupport+updated%3A2021-08-25..2022-07-14&type=Issues) | [@theomper](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atheomper+updated%3A2021-08-25..2022-07-14&type=Issues) | [@timotk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atimotk+updated%3A2021-08-25..2022-07-14&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awelcome+updated%3A2021-08-25..2022-07-14&type=Issues) | [@willingc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awillingc+updated%3A2021-08-25..2022-07-14&type=Issues) | [@wyphan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awyphan+updated%3A2021-08-25..2022-07-14&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2021-08-25..2022-07-14&type=Issues) + ## 1.2 ### 1.2.0 - 2021-11-04 From fe7e8b60a85d8e1694216622d706be4d325ccc16 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 18 Jul 2022 05:31:40 +0000 Subject: [PATCH 427/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 5667de081e..8bb594d464 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-07-04_05:28:29 +# VULN_SCAN_TIME=2022-07-18_05:31:38 USER root RUN apt-get update \ From 1576c3b8bf234d8ef9084be6b3e77e5ed3e23632 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 22 Jul 2022 05:21:30 +0000 Subject: [PATCH 428/898] Update pause version from 3.7 to 3.8 --- jupyterhub/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index d81ba3d683..178e509829 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -533,7 +533,7 @@ scheduling: # # If you update this, also update prePuller.pause.image.tag # - tag: "3.7" + tag: "3.8" pullPolicy: pullSecrets: [] replicas: 0 @@ -617,7 +617,7 @@ prePuller: # # If you update this, also update scheduling.userPlaceholder.image.tag # - tag: "3.7" + tag: "3.8" pullPolicy: pullSecrets: [] From 43c17098c1a90f376bbfedf7ea8982681261bdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B0=BD=ED=99=98?= Date: Fri, 22 Jul 2022 18:29:16 +0900 Subject: [PATCH 429/898] Add replace to KubeVersion.Minor for EKS support --- jupyterhub/templates/scheduling/user-scheduler/configmap.yaml | 2 +- jupyterhub/templates/scheduling/user-scheduler/deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml index b141806add..87b8f33d33 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -28,7 +28,7 @@ data: and configuration (v1beta1) of kube-scheduler. */}} config.yaml: | - {{- if ge (atoi .Capabilities.KubeVersion.Minor) 21 }} + {{- if ge (atoi ( .Capabilities.KubeVersion.Minor | replace "+" "" )) 21 }} apiVersion: kubescheduler.config.k8s.io/v1beta3 kind: KubeSchedulerConfiguration leaderElection: diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index ef41b4b86b..692549736c 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -47,7 +47,7 @@ spec: {{- end }} containers: - name: kube-scheduler - {{- if ge (atoi .Capabilities.KubeVersion.Minor) 21 }} + {{- if ge (atoi ( .Capabilities.KubeVersion.Minor | replace "+" "" )) 21 }} image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} {{- else }} # WARNING: The tag of this image is hardcoded, and the From ec68c85d0c4f30170d4867300aca4dafe9e6d7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ChangHwan=20Lee=20/=20=EC=9D=B4=EC=B0=BD=ED=99=98?= Date: Fri, 22 Jul 2022 23:11:43 +0900 Subject: [PATCH 430/898] Update jupyterhub/templates/scheduling/user-scheduler/configmap.yaml Co-authored-by: Erik Sundell --- .../templates/scheduling/user-scheduler/configmap.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml index 87b8f33d33..22ea9a8265 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -28,7 +28,14 @@ data: and configuration (v1beta1) of kube-scheduler. */}} config.yaml: | - {{- if ge (atoi ( .Capabilities.KubeVersion.Minor | replace "+" "" )) 21 }} + {{- /* + FIXME: We have added a workaround for EKS where + .Capabilities.KubeVersion.Minor can return a + string like "22+" instead of just "22". + + See https://github.com/aws/eks-distro/issues/1128. + */}} + {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} apiVersion: kubescheduler.config.k8s.io/v1beta3 kind: KubeSchedulerConfiguration leaderElection: From 63fa8a61f3b6bc7adeaa92b7619d4ef48d673c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ChangHwan=20Lee=20/=20=EC=9D=B4=EC=B0=BD=ED=99=98?= Date: Fri, 22 Jul 2022 23:11:48 +0900 Subject: [PATCH 431/898] Update jupyterhub/templates/scheduling/user-scheduler/deployment.yaml Co-authored-by: Erik Sundell --- .../templates/scheduling/user-scheduler/deployment.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 692549736c..d4f0536da5 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -47,7 +47,14 @@ spec: {{- end }} containers: - name: kube-scheduler - {{- if ge (atoi ( .Capabilities.KubeVersion.Minor | replace "+" "" )) 21 }} + {{- /* + FIXME: We have added a workaround for EKS where + .Capabilities.KubeVersion.Minor can return a + string like "22+" instead of just "22". + + See https://github.com/aws/eks-distro/issues/1128. + */}} + {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} {{- else }} # WARNING: The tag of this image is hardcoded, and the From 8a8dfff98cbf5b45e9b5c164269e97fbb3a5fb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=B0=BD=ED=99=98?= Date: Sat, 23 Jul 2022 17:52:45 +0900 Subject: [PATCH 432/898] Allow additional properties for scheduling.userScheduler.plugins --- jupyterhub/schema.yaml | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 14147193eb..9edb82322e 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2379,7 +2379,7 @@ properties: kube-scheduler binary running within the user-scheduler pod. plugins: type: object - additionalProperties: false + additionalProperties: true description: | These plugins refers to kube-scheduler plugins as documented [here](https://kubernetes.io/docs/reference/scheduling/config/). @@ -2387,29 +2387,6 @@ properties: The user-scheduler is really just a kube-scheduler configured in a way to pack users tight on nodes using these plugins. See values.yaml for information about the default plugins. - properties: - score: - type: object - additionalProperties: false - properties: - disabled: - type: array - items: - type: object - additionalProperties: false - properties: - name: - type: string - enabled: - type: array - items: - type: object - additionalProperties: false - properties: - name: - type: string - weight: - type: integer pluginConfig: type: array description: | From e8f96e8ce98e7c4fc8edb0aac2d6b6503915e5d4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sat, 23 Jul 2022 20:06:08 +0100 Subject: [PATCH 433/898] Add KubeSpawner changes to upgrade-1-to-2 --- .../administrator/upgrading/upgrade-1-to-2.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 92957cf657..caf58795e3 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -58,6 +58,9 @@ JupyterHub 2 adds RBAC for managing permissions in JupyterHub. The old permissions model of admin/non-admin still works but you should use [RBAC to assign the required privileges to users or services in future](https://jupyterhub.readthedocs.io/en/stable/rbac/index.html) +KubeSpawner has replaced the [`kubernetes`] library with [`kubernetes_asyncio`](https://github.com/tomplus/kubernetes_asyncio). +If you have extended the JupyterHub image and you rely on the kubernetes library you will need to modify your extensions. + See `TODO: link to Notable dependencies updated` in the changelog for more information on other upgraded hub components. @@ -67,6 +70,20 @@ in the changelog for more information on other upgraded hub components. The default singleuser server is [JupyterLab](https://jupyterlab.readthedocs.io/), running on [Jupyter server](https://jupyter-server.readthedocs.io/en/latest/). To switch back to Jupyter Notebook either configure/rebuild your singleuser image to default to notebook, or see [the documentation on user interfaces](user-interfaces) +## KubeSpawner disallows root users by default + +KubeSpawner will prevent processes executing as root in the singleuser container by default. +You must set `singleuser.allowPrivilegeEscalation: true` to enable root users. +For example, if you are using a [docker-stacks](https://jupyter-docker-stacks.readthedocs.io/) image and want to enable sudo: + +```yaml +singleuser: + allowPrivilegeEscalation: true + uid: 0 + extraEnv: + GRANT_SUDO: "1" +``` + ## Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) Z2JH now launches the container's default command (equivalent to setting `CMD` in a `Dockerfile`) instead of overriding it. From df85680fda45d7291ba89416e035fdc3a38f7e79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 05:03:53 +0000 Subject: [PATCH 434/898] build(deps): bump aquasecurity/trivy-action from 0.5.1 to 0.6.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.5.1 to 0.6.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/0105373003c89c494a3f436bd5efc57f3ac1ca20...503d3abc15463af68b817d685982721f134256a5) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 06b31fda1a..0d4450e942 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 + uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 + uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@0105373003c89c494a3f436bd5efc57f3ac1ca20 + uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 with: image-ref: rebuilt-image format: table From 009d65257175b5aa6b834234a0c602e8027bbc06 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 25 Jul 2022 05:26:47 +0000 Subject: [PATCH 435/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index a5a28c4d00..5975d9cdc8 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-04-11_01:44:28 +# VULN_SCAN_TIME=2022-07-25_05:26:46 RUN apk add --no-cache iptables From eb37e6c9656d550d86303fcb5fcd2a82ec129841 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 25 Jul 2022 05:27:20 +0000 Subject: [PATCH 436/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 3cf50e1108..ddd5a4e50f 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-04-25_01:46:01 +# VULN_SCAN_TIME=2022-07-25_05:27:18 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 2ea49dfd15783c15e91fdbc5791e9d992f5da2ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Jul 2022 22:16:32 +0000 Subject: [PATCH 437/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.1 → v2.37.2](https://github.com/asottile/pyupgrade/compare/v2.37.1...v2.37.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e7c0ecc51..1de97e6f2c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.37.1 + rev: v2.37.2 hooks: - id: pyupgrade args: From 7cb9656e262823f93aedea0c5941dcbdbcaae165 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Mon, 25 Jul 2022 18:31:04 -0400 Subject: [PATCH 438/898] Replace jhub with for consistency --- docs/source/jupyterhub/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 6b30f04ade..31f2b90d65 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -107,7 +107,7 @@ can try with `nano config.yaml`. a different terminal: ``` - kubectl get pod --namespace jhub + kubectl get pod --namespace ``` To remain sane we recommend that you enable autocompletion for kubectl From 63692f3ab5e1663583de6c3f308d045e3b7ab1b6 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 26 Jul 2022 13:55:00 +0100 Subject: [PATCH 439/898] allowPrivilegeEscalation: keep generic --- docs/source/administrator/upgrading/upgrade-1-to-2.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index caf58795e3..28c172e313 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -73,15 +73,11 @@ To switch back to Jupyter Notebook either configure/rebuild your singleuser imag ## KubeSpawner disallows root users by default KubeSpawner will prevent processes executing as root in the singleuser container by default. -You must set `singleuser.allowPrivilegeEscalation: true` to enable root users. -For example, if you are using a [docker-stacks](https://jupyter-docker-stacks.readthedocs.io/) image and want to enable sudo: +If you have configured sudo or some other privilege escalation method inside your singleuser image you must set `singleuser.allowPrivilegeEscalation: true`. ```yaml singleuser: allowPrivilegeEscalation: true - uid: 0 - extraEnv: - GRANT_SUDO: "1" ``` ## Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) From 221baa634f8e2ff4aa039695de29097c9e444edd Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 26 Jul 2022 13:59:06 +0100 Subject: [PATCH 440/898] allowPrivilegeEscalation: any escalation, not just root --- docs/source/administrator/upgrading/upgrade-1-to-2.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 28c172e313..ec7e411cb0 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -70,9 +70,10 @@ in the changelog for more information on other upgraded hub components. The default singleuser server is [JupyterLab](https://jupyterlab.readthedocs.io/), running on [Jupyter server](https://jupyter-server.readthedocs.io/en/latest/). To switch back to Jupyter Notebook either configure/rebuild your singleuser image to default to notebook, or see [the documentation on user interfaces](user-interfaces) -## KubeSpawner disallows root users by default +## KubeSpawner prevents privilege escalation such as sudo by default -KubeSpawner will prevent processes executing as root in the singleuser container by default. +By default processes cannot escalate their privileges. +For example, a user cannot use sudo to switch to root. If you have configured sudo or some other privilege escalation method inside your singleuser image you must set `singleuser.allowPrivilegeEscalation: true`. ```yaml From f34a55ff6a9e0a4818791c2af660aca409a2dc67 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 26 Jul 2022 17:40:20 +0200 Subject: [PATCH 441/898] docs: fix misc link redirects --- .../kubernetes/microsoft/step-zero-azure-autoscale.md | 2 +- docs/source/kubernetes/microsoft/step-zero-azure.md | 2 +- docs/source/kubernetes/ovh/step-zero-ovh.md | 6 +++--- docs/source/resources/community.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md index 0e0e9902f1..97e34f6b06 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md @@ -263,7 +263,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! -9. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), a tool +9. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool for accessing the Kubernetes API from the commandline: ``` diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 1865538477..0c38376228 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -227,7 +227,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! -8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), a tool +8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool for accessing the Kubernetes API from the commandline: ``` diff --git a/docs/source/kubernetes/ovh/step-zero-ovh.md b/docs/source/kubernetes/ovh/step-zero-ovh.md index 228edb114f..3d628f38ee 100644 --- a/docs/source/kubernetes/ovh/step-zero-ovh.md +++ b/docs/source/kubernetes/ovh/step-zero-ovh.md @@ -1,15 +1,15 @@ (ovh)= -# Kubernetes on [OVHcloud](https://www.ovh.ie/) (OVH) +# Kubernetes on [OVHcloud](https://www.ovhcloud.com/en-ie/) (OVH) -[OVHcloud](https://www.ovh.com/) is a leader in the hosted private cloud services space in Europe. +[OVHcloud](https://www.ovhcloud.com/en-ie/) is a leader in the hosted private cloud services space in Europe. They offer a managed Kubernetes service as well as a managed private registry for Docker images. This page describes how to create a Kubernetes cluster using the OVH Control Panel, and how to access the cluster using the command line with `kubectl`. -1. Log in to the [OVH Control Panel](https://www.ovh.com/auth/). +1. Log in to the [OVH Control Panel](https://www.ovhcloud.com/en-ie/auth/). ```{note} You first need to create an OVH account if you don't have one already. ``` diff --git a/docs/source/resources/community.md b/docs/source/resources/community.md index 04f2fecba3..08d0d51595 100644 --- a/docs/source/resources/community.md +++ b/docs/source/resources/community.md @@ -111,7 +111,7 @@ kubectl --namespace= get pod -o ``` You can find more information on what kinds of output you can generate at -[the kubectl information page](https://kubernetes.io/docs/reference/kubectl/overview/). +[the kubectl information page](https://kubernetes.io/docs/reference/kubectl/). (click and search for the text "Output Options") This is a community maintained list of organizations / people using the Zero to From e5eb2d316408bc0b6eaae3c5db7d85c15c95daf8 Mon Sep 17 00:00:00 2001 From: mgobec Date: Thu, 14 Jul 2022 00:04:57 -0700 Subject: [PATCH 442/898] Add revisionHistoryLimit to all deployments --- jupyterhub/schema.yaml | 10 ++++++++++ jupyterhub/templates/hub/deployment.yaml | 3 +++ jupyterhub/templates/proxy/autohttps/deployment.yaml | 3 +++ jupyterhub/templates/proxy/deployment.yaml | 3 +++ .../scheduling/user-scheduler/deployment.yaml | 3 +++ jupyterhub/values.yaml | 3 +++ 6 files changed, 25 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 14147193eb..8653e2d1f8 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -190,6 +190,14 @@ properties: additionalProperties: false required: [baseUrl] properties: + revisionHistoryLimit: &revisionHistoryLimit + type: [integer, "null"] + minimum: 0 + description: | + Configures the Deployment's `spec.revisionHistoryLimit`. + + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#revision-history-limit) + for more info. config: type: object additionalProperties: true @@ -1437,6 +1445,7 @@ properties: type: object additionalProperties: false properties: + revisionHistoryLimit: *revisionHistoryLimit chp: type: object additionalProperties: false @@ -2347,6 +2356,7 @@ properties: type: boolean description: | Enables the user scheduler. + revisionHistoryLimit: *revisionHistoryLimit replicas: type: integer description: | diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index 15a8d33a69..3e7a79f6de 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -5,6 +5,9 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: + {{- if not (eq nil .Values.hub.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.hub.revisionHistoryLimit }} + {{- end }} replicas: 1 selector: matchLabels: diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index 32d5e93af1..c9dcd65df8 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -8,6 +8,9 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: + {{- if not (eq nil .Values.proxy.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.proxy.revisionHistoryLimit }} + {{- end }} replicas: 1 selector: matchLabels: diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml index 9f58f945b4..93b9457901 100644 --- a/jupyterhub/templates/proxy/deployment.yaml +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -7,6 +7,9 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: + {{- if not (eq nil .Values.proxy.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.proxy.revisionHistoryLimit }} + {{- end }} replicas: 1 selector: matchLabels: diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index ef41b4b86b..3ce5e335d6 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -6,6 +6,9 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: + {{- if not (eq nil .Values.scheduling.userScheduler.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.scheduling.userScheduler.revisionHistoryLimit }} + {{- end }} replicas: {{ .Values.scheduling.userScheduler.replicas }} selector: matchLabels: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 93a3f6ff81..aa8055d006 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -26,6 +26,7 @@ imagePullSecrets: [] # ConfigurableHTTPProxy speaks with the actual ConfigurableHTTPProxy server in # the proxy pod. hub: + revisionHistoryLimit: config: JupyterHub: admin_access: true @@ -144,6 +145,7 @@ rbac: # proxy relates to the proxy pod, the proxy-public service, and the autohttps # pod and proxy-http service. proxy: + revisionHistoryLimit: secretToken: annotations: {} deploymentStrategy: @@ -403,6 +405,7 @@ singleuser: scheduling: userScheduler: enabled: true + revisionHistoryLimit: replicas: 2 logLevel: 4 # plugins are configured on the user-scheduler to make us score how we From b6d4501d3cc2861f5fdb7dcb70e426824885ae1e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 26 Jul 2022 19:31:47 +0200 Subject: [PATCH 443/898] Apply suggestions from code review Co-authored-by: Simon Li --- docs/source/kubernetes/ovh/step-zero-ovh.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/kubernetes/ovh/step-zero-ovh.md b/docs/source/kubernetes/ovh/step-zero-ovh.md index 3d628f38ee..67e0dc9d8e 100644 --- a/docs/source/kubernetes/ovh/step-zero-ovh.md +++ b/docs/source/kubernetes/ovh/step-zero-ovh.md @@ -1,8 +1,8 @@ (ovh)= -# Kubernetes on [OVHcloud](https://www.ovhcloud.com/en-ie/) (OVH) +# Kubernetes on [OVHcloud](https://www.ovhcloud.com/) (OVH) -[OVHcloud](https://www.ovhcloud.com/en-ie/) is a leader in the hosted private cloud services space in Europe. +[OVHcloud](https://www.ovhcloud.com/) is a leader in the hosted private cloud services space in Europe. They offer a managed Kubernetes service as well as a managed private registry for Docker images. From bd6dfd587fcc42c7bfebd933a8cae384dfae96dd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 27 Jul 2022 16:12:32 +0200 Subject: [PATCH 444/898] Add revisionHistoryLimit config everywhere it applies --- jupyterhub/schema.yaml | 5 ++++- jupyterhub/templates/image-puller/_helpers-daemonset.tpl | 3 +++ jupyterhub/templates/proxy/autohttps/deployment.yaml | 4 ++-- jupyterhub/templates/proxy/deployment.yaml | 4 ++-- .../templates/scheduling/user-placeholder/statefulset.yaml | 3 +++ jupyterhub/values.yaml | 5 ++++- tools/templates/lint-and-validate-values.yaml | 6 ++++++ 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 8653e2d1f8..71d2e5991d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1445,7 +1445,6 @@ properties: type: object additionalProperties: false properties: - revisionHistoryLimit: *revisionHistoryLimit chp: type: object additionalProperties: false @@ -1453,6 +1452,7 @@ properties: Configure the configurable-http-proxy (chp) pod managed by jupyterhub to route traffic both to itself and to user pods. properties: + revisionHistoryLimit: *revisionHistoryLimit networkPolicy: *networkPolicy-spec extraCommandLineFlags: type: array @@ -1754,6 +1754,7 @@ properties: description: | Configure the traefik proxy used to terminate TLS when 'autohttps' is enabled properties: + revisionHistoryLimit: *revisionHistoryLimit labels: type: object additionalProperties: false @@ -2521,6 +2522,7 @@ properties: enabled: type: boolean image: *image-spec + revisionHistoryLimit: *revisionHistoryLimit replicas: type: integer description: | @@ -2685,6 +2687,7 @@ properties: additionalProperties: false required: [hook, continuous] properties: + revisionHistoryLimit: *revisionHistoryLimit labels: type: object additionalProperties: false diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index 86e26e5436..6aa7c66a81 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -34,6 +34,9 @@ spec: type: RollingUpdate rollingUpdate: maxUnavailable: 100% + {{- if not (eq nil .Values.prePuller.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.prePuller.revisionHistoryLimit }} + {{- end }} template: metadata: labels: diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index c9dcd65df8..dca9249ae4 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -8,8 +8,8 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.proxy.revisionHistoryLimit) }} - revisionHistoryLimit: {{ .Values.proxy.revisionHistoryLimit }} + {{- if not (eq nil .Values.proxy.traefik.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.proxy.traefik.revisionHistoryLimit }} {{- end }} replicas: 1 selector: diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml index 93b9457901..b20547b715 100644 --- a/jupyterhub/templates/proxy/deployment.yaml +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -7,8 +7,8 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.proxy.revisionHistoryLimit) }} - revisionHistoryLimit: {{ .Values.proxy.revisionHistoryLimit }} + {{- if not (eq nil .Values.proxy.chp.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.proxy.chp.revisionHistoryLimit }} {{- end }} replicas: 1 selector: diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index e928966d69..6632133ce2 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -16,6 +16,9 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} spec: podManagementPolicy: Parallel + {{- if not (eq nil .Values.scheduling.userPlaceholder.revisionHistoryLimit) }} + revisionHistoryLimit: {{ .Values.scheduling.userPlaceholder.revisionHistoryLimit }} + {{- end }} replicas: {{ .Values.scheduling.userPlaceholder.replicas }} selector: matchLabels: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index aa8055d006..a83133367f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -145,7 +145,6 @@ rbac: # proxy relates to the proxy pod, the proxy-public service, and the autohttps # pod and proxy-http service. proxy: - revisionHistoryLimit: secretToken: annotations: {} deploymentStrategy: @@ -189,6 +188,7 @@ proxy: # chp relates to the proxy pod, which is responsible for routing traffic based # on dynamic configuration sent from JupyterHub to CHP's REST API. chp: + revisionHistoryLimit: containerSecurityContext: runAsUser: 65534 # nobody user runAsGroup: 65534 # nobody group @@ -239,6 +239,7 @@ proxy: # traefik relates to the autohttps pod, which is responsible for TLS # termination when proxy.https.type=letsencrypt. traefik: + revisionHistoryLimit: containerSecurityContext: runAsUser: 65534 # nobody user runAsGroup: 65534 # nobody group @@ -539,6 +540,7 @@ scheduling: tag: "3.7" pullPolicy: pullSecrets: [] + revisionHistoryLimit: replicas: 0 labels: {} annotations: {} @@ -574,6 +576,7 @@ scheduling: # prePuller relates to the hook|continuous-image-puller DaemonsSets prePuller: + revisionHistoryLimit: labels: {} annotations: {} resources: {} diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index e60717ae84..4dda6ce095 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -8,6 +8,7 @@ imagePullSecret: imagePullSecrets: [a, b] hub: + revisionHistoryLimit: 1 config: # We pin these to avoid generating changes which cause diffs ConfigurableHTTPProxy: @@ -221,6 +222,7 @@ proxy: http: https: chp: + revisionHistoryLimit: 1 extraCommandLineFlags: - "--auto-rewrite" - "--mock-flag {{ .Values.proxy.chp.resources.requests.memory }}" @@ -261,6 +263,7 @@ proxy: minAvailable: 1 extraPodSpec: *extraPodSpec traefik: + revisionHistoryLimit: 1 labels: *labels resources: *resources extraEnv: *extraEnv @@ -460,6 +463,7 @@ singleuser: scheduling: userScheduler: enabled: true + revisionHistoryLimit: 1 replicas: 1 logLevel: 10 image: *image @@ -492,6 +496,7 @@ scheduling: enabled: true userPlaceholder: enabled: true + revisionHistoryLimit: 1 replicas: 1 resources: *resources corePods: @@ -512,6 +517,7 @@ scheduling: matchNodePurpose: require prePuller: + revisionHistoryLimit: 1 extraTolerations: - key: mock-taint-key-prePuller operator: Equal From 6e7e997a27e60e9e72aab516d45bf256a1cff464 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 27 Jul 2022 16:36:41 +0200 Subject: [PATCH 445/898] Use typeIs to avoid needing helm 3.8+ instead of helm 3.5+ Apparently the equality operators `eq` and `ne` will error if passed `nil` and 5 for example because they are of diffenre types. --- jupyterhub/templates/hub/deployment.yaml | 2 +- jupyterhub/templates/image-puller/_helpers-daemonset.tpl | 2 +- jupyterhub/templates/proxy/autohttps/deployment.yaml | 2 +- jupyterhub/templates/proxy/deployment.yaml | 2 +- .../templates/scheduling/user-placeholder/statefulset.yaml | 2 +- jupyterhub/templates/scheduling/user-scheduler/deployment.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml index 3e7a79f6de..d6e1c63ed8 100644 --- a/jupyterhub/templates/hub/deployment.yaml +++ b/jupyterhub/templates/hub/deployment.yaml @@ -5,7 +5,7 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.hub.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.hub.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.hub.revisionHistoryLimit }} {{- end }} replicas: 1 diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index 6aa7c66a81..1fe8276e3e 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -34,7 +34,7 @@ spec: type: RollingUpdate rollingUpdate: maxUnavailable: 100% - {{- if not (eq nil .Values.prePuller.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.prePuller.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.prePuller.revisionHistoryLimit }} {{- end }} template: diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml index dca9249ae4..f76f3efbfa 100644 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -8,7 +8,7 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.proxy.traefik.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.proxy.traefik.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.proxy.traefik.revisionHistoryLimit }} {{- end }} replicas: 1 diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml index b20547b715..2b35382446 100644 --- a/jupyterhub/templates/proxy/deployment.yaml +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -7,7 +7,7 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.proxy.chp.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.proxy.chp.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.proxy.chp.revisionHistoryLimit }} {{- end }} replicas: 1 diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index 6632133ce2..e0f6f5958c 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -16,7 +16,7 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} spec: podManagementPolicy: Parallel - {{- if not (eq nil .Values.scheduling.userPlaceholder.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.scheduling.userPlaceholder.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.scheduling.userPlaceholder.revisionHistoryLimit }} {{- end }} replicas: {{ .Values.scheduling.userPlaceholder.replicas }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 3ce5e335d6..677a56828f 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -6,7 +6,7 @@ metadata: labels: {{- include "jupyterhub.labels" . | nindent 4 }} spec: - {{- if not (eq nil .Values.scheduling.userScheduler.revisionHistoryLimit) }} + {{- if typeIs "int" .Values.scheduling.userScheduler.revisionHistoryLimit }} revisionHistoryLimit: {{ .Values.scheduling.userScheduler.revisionHistoryLimit }} {{- end }} replicas: {{ .Values.scheduling.userScheduler.replicas }} From e426f3efcd999cc53e350fcc09471fd814aaedee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 05:05:10 +0000 Subject: [PATCH 446/898] build(deps): bump aquasecurity/trivy-action from 0.6.0 to 0.6.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.6.0 to 0.6.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/503d3abc15463af68b817d685982721f134256a5...81b9a6f5abb1047d697af7a3ca18c13f55a97315) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0d4450e942..e7938835de 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 + uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 + uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@503d3abc15463af68b817d685982721f134256a5 + uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 with: image-ref: rebuilt-image format: table From 40272b13b3329cd03c1be39953587d7a8a8b5dac Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 1 Aug 2022 11:24:05 +0200 Subject: [PATCH 447/898] Restore jupyterhub-singleuser as the default command update docs to explain explicit `cmd: null` use case --- .../customizing/user-environment.md | 42 ++++++++++++------- jupyterhub/schema.yaml | 8 +++- jupyterhub/values.yaml | 2 +- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 84c0fbd655..14e2903474 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -44,6 +44,9 @@ image containing useful tools and libraries for data science, complete these ste # https://github.com/jupyter/docker-stacks/tree/HEAD/datascience-notebook/Dockerfile name: jupyter/datascience-notebook tag: latest + # `cmd: null` allows the custom CMD of the Jupyter docker-stacks to be used + # which performs further customization on startup. + cmd: null ``` ```{note} @@ -244,10 +247,9 @@ FROM jupyter/minimal-notebook:latest RUN pip install --no-cache-dir astropy # set the default command of the image, -# if the parent image will not launch a jupyterhub singleuser server. -# The JupyterHub "Docker stacks" do not need to be overridden. -# Set either here or in `singleuser.cmd` in your values.yaml -# CMD ["jupyterhub-singleuser"] +# if you want to launch more complex startup than the default `juptyerhub-singleuser`. +# To launch an image's custom CMD instead of the default `jupyterhub-singleuser` +# set `singleuser.cmd: null` in your config.yaml. ``` ```{note} @@ -529,25 +531,33 @@ this is best done in the ENTRYPOINT of the image, and not in the CMD, so that overriding the command does not skip your preparation. ``` -By default, zero-to-jupyterhub will launch the default CMD that is specified in your chosen image, -respecting any startup customization that image may have. -If the image doesn't launch `jupyterhub-singleuser` by default, -you will additionally need to specify `singleuser.cmd` -in your `values.yaml` as the command to launch, -so that it ultimately launches `jupyterhub-singleuser`. -The simplest version: +By default, zero-to-jupyterhub will launch the command `jupyterhub-singleuser`. +If you have an image (such as `jupyter/scipy-notebook` and other Jupyter Docker stacks) +that defines a CMD with startup customization and ultimately launches `jupyterhub-singleuser`, +you can chose to launch the image's default CMD instead by setting: ```yaml singleuser: - cmd: jupyterhub-singleuser + cmd: null ``` -```{versionchanged} 2.0 -Prior to 2.0, the default behavior of zero-to-jupyterhub was to launch `jupyterhub-singleuser` explicitly, -ignoring what was in the image. -The default command is now whatever the image runs by default. +Alternately, you can specify an explicit custom command as a string or list of strings: + +```yaml +singleuser: + cmd: + - /usr/local/bin/custom-command + - "--flag" + - "--other-flag" ``` +:::{note} +Docker has `ENTRYPOINT` and `CMD`, +which k8s calls `command` and `args`. +zero-to-jupyterhub always respects the ENTRYPOINT of the image, +and setting `singleuser.cmd` only overrides the CMD. +::: + ## Disable specific JupyterLab extensions Sometimes you want to temporarily disable a JupyterLab extension on a JupyterHub diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 9edb82322e..ad29009e28 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1019,7 +1019,7 @@ properties: extraPorts: type: array description: | - Extra ports to add to the Hub Service object besides `hub` / `8081`. + Extra ports to add to the Hub Service object besides `hub` / `8081`. This should be an array that includes `name`, `port`, and `targetPort`. See [Multi-port Services](https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services) for more details. loadBalancerIP: @@ -2124,6 +2124,10 @@ properties: description: | Passthrough configuration for [KubeSpawner.cmd](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.cmd). + The default is "jupyterhub-singleuser". + Use `cmd: null` to launch a custom CMD from the image, + which must launch jupyterhub-singleuser eventually. + For example: Jupyter's docker-stacks images. defaultUrl: type: [string, "null"] description: | @@ -2636,7 +2640,7 @@ properties: description: | The path type to use. The default value is 'Prefix'. - See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) + See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) for more details about path types. tls: type: array diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 178e509829..dc99970381 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -394,7 +394,7 @@ singleuser: extraResource: limits: {} guarantees: {} - cmd: + cmd: jupyterhub-singleuser defaultUrl: extraPodConfig: {} profileList: [] From cbdd5403877b47bbeeed8b5b21bdbf3bc3b93e2a Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 1 Aug 2022 11:30:21 +0200 Subject: [PATCH 448/898] ensure we respect explicit `singleuser.cmd: null` --- jupyterhub/files/hub/jupyterhub_config.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index a6a4fa4d7f..1ce7412471 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -408,8 +408,13 @@ def camelCaseify(s): c.JupyterHub.load_roles.append(role) +# respect explicit null command (distinct from unspecified) +# this avoids relying on KubeSpawner.cmd's default being None +_unspecified = object() +specified_cmd = get_config("singleuser.cmd", _unspecified) +if specified_cmd is not _unspecified: + c.Spawner.cmd = specified_cmd -set_config_if_not_none(c.Spawner, "cmd", "singleuser.cmd") set_config_if_not_none(c.Spawner, "default_url", "singleuser.defaultUrl") cloud_metadata = get_config("singleuser.cloudMetadata", {}) From 8ceaaa74f234a5865c79e2202c0666e093674dcc Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 1 Aug 2022 13:30:36 +0200 Subject: [PATCH 449/898] or an equivalent process Co-authored-by: Simon Li --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ad29009e28..670cdf759d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2126,7 +2126,7 @@ properties: [KubeSpawner.cmd](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.cmd). The default is "jupyterhub-singleuser". Use `cmd: null` to launch a custom CMD from the image, - which must launch jupyterhub-singleuser eventually. + which must launch jupyterhub-singleuser or an equivalent process eventually. For example: Jupyter's docker-stacks images. defaultUrl: type: [string, "null"] From 73a1f5730bdb9068db90f548c6e77c94292114da Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 1 Aug 2022 13:34:38 +0200 Subject: [PATCH 450/898] Remove upgrade note on singleuser.cmd default it's not the default anymore --- .../administrator/upgrading/upgrade-1-to-2.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index ec7e411cb0..8a3d35e709 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -81,19 +81,6 @@ singleuser: allowPrivilegeEscalation: true ``` -## Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) - -Z2JH now launches the container's default command (equivalent to setting `CMD` in a `Dockerfile`) instead of overriding it. -This ensures that containers that use a custom start command to configure their environment, such as some -[Jupyter Docker Stacks](https://jupyter-docker-stacks.readthedocs.io/en/latest/) -images, will work without any changes. -To restore the old behaviour set: - -```yaml -singleuser: - cmd: jupyterhub-singleuser -``` - ## Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) Previously if `hub.config` was used to configure some JupyterHub traitlets it would override any custom configuration files mounted into `jupyterhub_config.d` in the hub container. From 37b248bc10a5dd910961e41e6038138ef9eef58a Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 2 Aug 2022 00:12:26 +0100 Subject: [PATCH 451/898] Fix step-zero-ovh 404 --- docs/source/kubernetes/ovh/step-zero-ovh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/ovh/step-zero-ovh.md b/docs/source/kubernetes/ovh/step-zero-ovh.md index 67e0dc9d8e..95be1cb2c6 100644 --- a/docs/source/kubernetes/ovh/step-zero-ovh.md +++ b/docs/source/kubernetes/ovh/step-zero-ovh.md @@ -9,7 +9,7 @@ They offer a managed Kubernetes service as well as a managed private registry fo This page describes how to create a Kubernetes cluster using the OVH Control Panel, and how to access the cluster using the command line with `kubectl`. -1. Log in to the [OVH Control Panel](https://www.ovhcloud.com/en-ie/auth/). +1. Log in to the [OVH Control Panel](https://www.ovh.com/auth/). ```{note} You first need to create an OVH account if you don't have one already. ``` From cee42d7ca2bbf61c9e817dc22d580cee5ef52bfd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 23:15:50 +0000 Subject: [PATCH 452/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.2 → v2.37.3](https://github.com/asottile/pyupgrade/compare/v2.37.2...v2.37.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1de97e6f2c..9688201e0c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.37.2 + rev: v2.37.3 hooks: - id: pyupgrade args: From e265e2b13513d0e9bdab418eee35d023e8fa1ae8 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Tue, 2 Aug 2022 09:17:24 +0200 Subject: [PATCH 453/898] docs: Remove separate autoscaler docs for azure The autoscaling feature is now in production and can be enabled with a single flag. This has been incorporated into the main azure docs. --- .../microsoft/step-zero-azure-autoscale.md | 375 ------------------ 1 file changed, 375 deletions(-) delete mode 100644 docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md diff --git a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md b/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md deleted file mode 100644 index 97e34f6b06..0000000000 --- a/docs/source/kubernetes/microsoft/step-zero-azure-autoscale.md +++ /dev/null @@ -1,375 +0,0 @@ -(microsoft-azure-autoscale)= - -# Kubernetes on Microsoft Azure Kubernetes Service (AKS) with Autoscaling - -```{warning} -These instructions involve parts of the Azure command line that are in preview, hence the following documentation is subject to change. -``` - -You can create a Kubernetes cluster [either through the Azure portal website, or using the Azure command line tools](https://docs.microsoft.com/en-us/azure/aks/). - -This page describes the commands required to setup a Kubernetes cluster using the command line. -If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal). - -1. Prepare your Azure shell environment. You have two options, one is to use - the Azure interactive shell, the other is to install the Azure command-line - tools locally. Instructions for each are below. - - - **Using the Azure interactive shell**. The [Azure Portal](https://portal.azure.com) - contains an interactive shell that you can use to communicate with your - Kubernetes cluster. To access this shell, go to [portal.azure.com](https://portal.azure.com) - and click on the button below. - - ```{image} ../../_static/images/azure/cli_start.png - :align: center - ``` - - ```{note} - * If you get errors like `could not retrieve token from local cache`, - try refreshing your browser window. - * The first time you do this, you'll be asked to create a storage - account where your shell filesystem will live. - ``` - - - **Install command-line tools locally**. You can access the Azure CLI via - a package that you can install locally. - - To do so, first follow the [installation instructions](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) in the - Azure documentation. Then run the following command to connect your local - CLI with your account: - - ``` - az login - ``` - - You'll need to open a browser and follow the instructions in your terminal - to log in. - -2. Activate the correct subscription. Azure uses the concept - of **subscriptions** to manage spending. You can - get a list of subscriptions your account has access to by running: - - ``` - az account list --refresh --output table - ``` - - Pick the subscription you want to use for creating the cluster, and set that - as your default. - If you only have one subscription you can ignore this step. - - ``` - az account set --subscription - ``` - -3. Setup the CLI for Autoscaling features. - First install the [aks-preview](https://github.com/Azure/azure-cli-extensions/tree/HEAD/src/aks-preview) CLI extension. - This will grant access to new commands. - - ``` - az extension add --name aks-preview - ``` - - We then need to register the scale set feature. - - ``` - az feature register --name VMSSPreview --namespace Microsoft.ContainerService - ``` - - A VMSS is a [Virtual Machine Scale Set](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/overview), that is to say an autoscalable set of virtual machines. - - The previous command will take a while to register. - Use the following command to check it's status. - - ``` - az feature list \ - --output table \ - --query "[?contains(name, 'Microsoft.ContainerService/VMSSPreview')].{Name:name,State:properties.state}" - ``` - - Once the VMSSPreview feature has been registered, refresh the registration with the following command. - - ``` - az provider register --namespace Microsoft.ContainerService - ``` - -4. Create a resource group. Azure uses the concept of - **resource groups** to group related resources together. - We need to create a resource group in a given data center location. We will create - computational resources _within_ this resource group. - - ``` - az group create \ - --name= \ - --location= \ - --output table - ``` - - where: - - - `--name` specifies the name of your resource group. We recommend using something - that uniquely identifies this hub. For example, if you are creating a resource group - for UC Berkeley's 2018 Spring Data100 Course, you may give it a - `` of `ucb_2018sp_data100_hub`. - - `--location` specifies the location of the data center you want your resource to be in. - For options, see the - [Azure list of locations that support AKS](https://docs.microsoft.com/en-us/azure/aks/quotas-skus-regions#region-availability). - - `--output table` specifies that the output should be in human readable - format, rather than the default JSON output. We shall use this with most - commands when executing them by hand. - - Consider [setting a cloud budget](https://docs.microsoft.com/en-us/partner-center/set-an-azure-spending-budget-for-your-customers) - for your Azure account in order to make sure you don't accidentally - spend more than you wish to. - -5. Choose a cluster name. - - In the following steps we'll run commands that ask you to input a cluster - name. We recommend using something descriptive and short. We'll refer to - this as `` for the remainder of this section. - - The next step will create a few files on your filesystem, so first create - a folder in which these files will go. We recommend giving it the same - name as your cluster: - - ``` - mkdir - cd - ``` - -6. Create an ssh key to secure your cluster. - - ``` - ssh-keygen -f ssh-key- - ``` - - It will prompt you to add a password, which you can leave empty if you wish. - This will create a public key named `ssh-key-.pub` and a private key named - `ssh-key-`. Make sure both go into the folder we created earlier, - and keep both of them safe! - - This command will also print out something to your terminal screen. You - don't need to do anything with this text. - -7. Create a virtual network and sub-network. - - Kubernetes does not by default come with a controller that enforces `networkpolicy` resources. - `networkpolicy` resources are important as they define how Kubernetes pods can securely communicate with one another and the outside sources, for example, the internet. - - To enable this in Azure, we must first create a [Virtual Network](https://docs.microsoft.com/en-gb/azure/virtual-network/virtual-networks-overview) with Azure's own network policies enabled. - - This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://projectcalico.docs.tigera.io/about/about-calico) network policies. - - ``` - az network vnet create \ - --resource-group \ - --name \ - --address-prefixes 10.0.0.0/8 \ - --subnet-name \ - --subnet-prefix 10.240.0.0/16 - ``` - - where: - - - `--resource-group` is the ResourceGroup you created - - `--name` is the name you want to assign to your virtual network, for example, `hub-vnet` - - `--address-prefixes` are the IP address prefixes for your virtual network - - `--subnet-name` is your desired name for your subnet, for example, `hub-subnet` - - `--subnet-prefixes` are the IP address prefixes in [CIDR format](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) for the subnet - - We will now retrieve the application IDs of the VNet and subnet we just created and save them to bash variables. - - ``` - VNET_ID=$(az network vnet show \ - --resource-group \ - --name \ - --query id \ - --output tsv) - SUBNET_ID=$(az network vnet subnet show \ - --resource-group \ - --vnet-name \ - --name \ - --query id \ - --output tsv) - ``` - - We will create an Azure Active Directory (Azure AD) [service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals) for use with the cluster, and assign the [Contributor role](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor) for use with the VNet. - Make sure `SERVICE-PRINCIPAL-NAME` is something recognisable, for example, `binderhub-sp`. - - ``` - SP_PASSWD=$(az ad sp create-for-rbac \ - --name \ - --role Contributor \ - --scope $VNET_ID \ - --query password \ - --output tsv) - SP_ID=$(az ad sp show \ - --id http:// \ - --query appId \ - --output tsv) - ``` - - You will need Owner role on your subscription for this step to succeed. - -8. Create an AKS cluster. - - The following command will request a Kubernetes cluster within the resource - group that we created earlier. - - ``` - az aks create --name \ - --resource-group \ - --ssh-key-value ssh-key-.pub \ - --node-count 3 \ - --node-vm-size Standard_D2s_v3 \ - --enable-vmss \ - --enable-cluster-autoscaler \ - --min-count 3 \ - --max-count 6 \ - --kubernetes-version 1.12.7 \ - --service-principal $SP_ID \ - --client-secret $SP_PASSWD \ - --dns-service-ip 10.0.0.10 \ - --docker-bridge-address 172.17.0.1/16 \ - --network-plugin azure \ - --network-policy azure \ - --service-cidr 10.0.0.0/16 \ - --vnet-subnet-id $SUBNET_ID \ - --output table - ``` - - where: - - - `--name` is the name you want to use to refer to your cluster - - `--resource-group` is the ResourceGroup you created - - `--ssh-key-value` is the ssh public key created - - `--node-count` is the number of nodes you want in your Kubernetes cluster - - `--node-vm-size` is the size of the nodes you want to use, which varies based on - what you are using your cluster for and how much RAM/CPU each of your users need. - There is a [list of all possible node sizes](https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-sizes-specs) - for you to choose from, but not all might be available in your location. - If you get an error whilst creating the cluster you can try changing either the region or the node size. - - `--enable-vmss` deploys the cluster as a scale set. - - `--enable-cluster-autoscaler` installs a [Cluster Autoscaler](https://github.com/kubernetes/autoscaler/tree/HEAD/cluster-autoscaler) onto the cluster (though counterintuitively, does not enable it!). - - `--min-count`/`--max-count` are the minimum/maximum number of nodes in the cluster at any time. - - `--kubernetes-version` installs a specific version of Kubernetes onto the cluster. To autoscale, we require `>= v 1.12.4`, though it's recommended to use the most recent version available (you can find out what the most recent version of kubernetes available is by running `az aks get-versions --location `). - - `--service-principal` is the application ID of the service principal we created - - `--client-secret` is the password for the service principal we created - - `--dns-service-ip` is an IP address assigned to the [Kubernetes DNS service](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/) - - `--docker-bridge-address` is a specific IP address and netmask for the Docker bridge, using standard CIDR notation - - `--network-plugin` is the Kubernetes network plugin to use. In this example, we have used Azure's own implementation. - - `--network-policy` is the Kubernetes network policy to use. In this example, we have used Azure's own implementation. - - `--service-cidr` is a CIDR notation IP range from which to assign service cluster IPs - - `vnet-subnet-id` is the application ID of the subnet we created - - This should take a few minutes and provide you with a working Kubernetes cluster! - -9. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool - for accessing the Kubernetes API from the commandline: - - ``` - az aks install-cli - ``` - - Note: kubectl is already installed in Azure Cloud Shell. - -10. Get credentials from Azure for `kubectl` to work: - - ``` - az aks get-credentials \ - --name \ - --resource-group \ - --output table - ``` - - where: - - - `--name` is the name you gave your cluster - - `--resource-group` is the ResourceGroup you created - - This automatically updates your Kubernetes client configuration file. - -11. Check if your cluster is fully functional - - ``` - kubectl get node - ``` - - The response should list three running nodes and their Kubernetes versions! - Each node should have the status of `Ready`, note that this may take a - few moments. - -12. Enabling Autoscaling - - We now move to the Azure Portal to enable autoscaling and set rules to manage the Cluster Autoscaler. - - First we need to register [Microsoft Insights](https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview) for use on the active subscription. - - ``` - az provider register --namespace microsoft.insights - ``` - - To check the status of the registration, run the following command: - - ``` - az provider show -n microsoft.insights - ``` - - Once the application has been registered, navigate to your active subscription on the [Portal](https://portal.azure.com/). - - Under "Resources", select the VMSS. - It should be named something like `aks-nodepool1--vmss`. - - ```{image} ../../_static/images/azure/select_vmss.png - :align: center - ``` - - From the left-hand menu, select "Scaling". - Click the blue "Enable autoscaling" button and an autogenerated form for a scale condition will appear. - We will add two new rules to this condition: - - - Increase the instance count by 1 when the average CPU usage over 10 minutes is greater than 70% - - Decrease the instance count by 1 when the average CPU usage over 10 minutes is less than 5% - - ```{image} ../../_static/images/azure/scale_condition.png - :align: center - ``` - - Make sure the "Scale based on metric" option is selected and click "+ Add new rule", another autogenerated form will appear. - This will be pre-filled with the required settings to fulfill our first rule, so save it by clicking "Update" and click "+ Add new rule" again. - - ```{image} ../../_static/images/azure/scale_out.png - :align: center - ``` - - The second form needs to be edited for the second rule to decrease the instance count by 1 when the average CPU usage over 10 minutes is less than 5%. - Save this rule and then save the overall scale condition, the cluster will be updated automatically. - - ```{image} ../../_static/images/azure/scale_in.png - :align: center - ``` - - This form can also be used to change `--node-count`/`--min-count`/`--max-count` that was set previously by using the "Instance limits" section of the scale condition ("Default", "Minimum" and "Maximum" respectively). - - If you prefer to use the command line, you can run the following: - - ``` - az aks update \ - --name \ - --resource-group \ - --update-cluster-autoscaler \ - --min-count \ - --max-count \ - --output table - ``` - - **Both** `--min-count` and `--max-count` must be defined. - -```{note} -If you create the cluster using the Azure Portal you must enable RBAC. -RBAC is enabled by default when using the command line tools. -``` - -Congrats. Now that you have your Kubernetes cluster running, it's time to -begin {ref}`setup-helm`. - -[azure resource group]: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview#resource-groups From 35deeec6687a801df1314f88820274c01fba9326 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Tue, 2 Aug 2022 09:18:24 +0200 Subject: [PATCH 454/898] docs: Add a note to recommended locations that feature a broader list of VM node sizes --- docs/source/kubernetes/microsoft/step-zero-azure.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 0c38376228..f436fbf432 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -78,6 +78,9 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta - `--location` specifies the location of the data center you want your resource to be in. In this case, we used the `centralus` location. For other options, see the [Azure list of locations that support AKS](https://docs.microsoft.com/en-us/azure/aks/quotas-skus-regions#region-availability). + Note that not all locations offer all VM sizes. To see a list of recommended locations, go to + [Azure Portal > Virtual Machines](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Compute%2FVirtualMachines), + click on "create.." and see the list of recommended locations in the drop down list for `Region`. - `--output table` specifies that the output should be in human readable format, rather than the default JSON output. We shall use this with most commands when executing them by hand. From 534a8939e670329993614a6ebc75136c54d062ca Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Tue, 2 Aug 2022 09:20:35 +0200 Subject: [PATCH 455/898] docs: According to [azure-cli issue #19179][1], --id http// has ben deprecated and only appId should be used. Update the docs accordingly. [1]: https://github.com/Azure/azure-cli/issues/19179#issuecomment-900785318 --- docs/source/kubernetes/microsoft/step-zero-azure.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index f436fbf432..a19b7914e4 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -170,9 +170,9 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --scopes $VNET_ID \ --query password \ --output tsv) - SP_ID=$(az ad sp show \ - --id http:// \ - --query appId \ + SP_ID=$(az ad app list \ + --filter "displayname eq ''" \ + --query [0].appId \ --output tsv) ``` From 1184f128f30f3aed0c239c952db1e6ab2e667573 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Tue, 2 Aug 2022 09:21:09 +0200 Subject: [PATCH 456/898] docs: Incorporate instructions to enable autoscaling feature into main azure docs --- .../kubernetes/microsoft/step-zero-azure.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index a19b7914e4..56643c28a9 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -230,6 +230,32 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! +Optionally, enable autoscaling: + + where: + - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster + - `--min-count 1` is the minimum node count + - `--max-count 4` is the maximum node count + +You can also enable autoscaling feature later, with: + + ``` + SP_POOLNAME=$(az aks nodepool list \ + --resource-group \ + --cluster-name \ + --query [0].name \ + --output tsv) + az aks nodepool update \ + --name $SP_POOLNAME \ + --cluster-name \ + --resource-group \ + --enable-cluster-autoscaler \ + --min-count 1 \ + --max-count 3 + ``` + +or update the parameters with `az aks --update-cluster-autoscaler`. + 8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool for accessing the Kubernetes API from the commandline: From 3032171ca46ebc3928cdb5a928e42c3a6d6f1d14 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Aug 2022 07:25:43 +0000 Subject: [PATCH 457/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../kubernetes/microsoft/step-zero-azure.md | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 56643c28a9..edbff73090 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -232,27 +232,28 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta Optionally, enable autoscaling: - where: - - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster - - `--min-count 1` is the minimum node count - - `--max-count 4` is the maximum node count +where: + +- `--enable-cluster-autoscaler` enables autoscaling feature for your cluster +- `--min-count 1` is the minimum node count +- `--max-count 4` is the maximum node count You can also enable autoscaling feature later, with: - ``` - SP_POOLNAME=$(az aks nodepool list \ - --resource-group \ - --cluster-name \ - --query [0].name \ - --output tsv) - az aks nodepool update \ - --name $SP_POOLNAME \ - --cluster-name \ +``` +SP_POOLNAME=$(az aks nodepool list \ --resource-group \ - --enable-cluster-autoscaler \ - --min-count 1 \ - --max-count 3 - ``` + --cluster-name \ + --query [0].name \ + --output tsv) +az aks nodepool update \ +--name $SP_POOLNAME \ +--cluster-name \ +--resource-group \ +--enable-cluster-autoscaler \ +--min-count 1 \ +--max-count 3 +``` or update the parameters with `az aks --update-cluster-autoscaler`. From a17ae921c41a83aa53d638532f9eeb6e7fd922e6 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Tue, 2 Aug 2022 09:28:39 +0200 Subject: [PATCH 458/898] fix: Remove missing link --- docs/source/kubernetes/setup-kubernetes.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md index d7f0d6d002..f6826174f3 100644 --- a/docs/source/kubernetes/setup-kubernetes.md +++ b/docs/source/kubernetes/setup-kubernetes.md @@ -13,7 +13,6 @@ Choose one option and proceed. google/step-zero-gcp microsoft/step-zero-azure -microsoft/step-zero-azure-autoscale amazon/step-zero-aws amazon/step-zero-aws-eks redhat/step-zero-openshift From eec8402d22f51d2ed1cc42e4a3c20249f9875f5f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Aug 2022 10:10:12 +0200 Subject: [PATCH 459/898] Make reused schema note agnostic of resource type --- jupyterhub/schema.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 71d2e5991d..6897edc248 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -194,7 +194,8 @@ properties: type: [integer, "null"] minimum: 0 description: | - Configures the Deployment's `spec.revisionHistoryLimit`. + Configures the resource's `spec.revisionHistoryLimit`. This is + available for Deployment, StatefulSet, and DaemonSet resources. See the [Kubernetes docs](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#revision-history-limit) for more info. From d9d161ed6bd67c70d277838d628c222719ad08cc Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 2 Aug 2022 15:19:46 +0200 Subject: [PATCH 460/898] JupyterHub 3.0.0b1 --- images/hub/requirements.in | 9 +++++---- images/hub/requirements.txt | 8 +++++--- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index cd155a866d..73347fd745 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -1,10 +1,11 @@ -# This file is automatically maintained by Dependabot, as configured in -# .github/dependabot.yaml. It can also be updated by a command described in the +# This file is the input to requirements.txt, +# which is is automatically maintained by Dependabot, +# as configured in .github/dependabot.yaml. +# It can also be updated by a command described in the # README.md file. -# # JupyterHub itself -jupyterhub==2.3.1 +jupyterhub==3.0.0b1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b2a0fcf534..1daa6acced 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -38,8 +38,6 @@ charset-normalizer==2.1.0 # requests cryptography==37.0.4 # via pyopenssl -entrypoints==0.4 - # via jupyterhub escapism==1.0.1 # via # jupyterhub-kubespawner @@ -54,6 +52,8 @@ idna==3.3 # via # requests # yarl +importlib-metadata==4.12.0 + # via jupyterhub jinja2==3.1.2 # via # jupyterhub @@ -64,7 +64,7 @@ jsonschema==4.7.2 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==2.3.1 +jupyterhub==3.0.0b1 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -207,6 +207,8 @@ urllib3==1.26.10 # requests yarl==1.7.2 # via aiohttp +zipp==3.8.1 + # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 47b40a2e39..11566d4d72 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # This file is automatically maintained by Dependabot, as configured in # .github/dependabot.yaml. -jupyterhub==2.3.1 +jupyterhub==3.0.0b1 nbgitpuller==1.1.0 diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index e30dfcfa80..1c0507f97c 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "2.3.1" +appVersion: "3.0.0-beta.1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From ea8621bb70c01873633d8a115b6c00b0af8c5edb Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 2 Aug 2022 15:28:53 +0200 Subject: [PATCH 461/898] is is typo Co-authored-by: Simon Li --- images/hub/requirements.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 73347fd745..188d128d9d 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -1,5 +1,5 @@ # This file is the input to requirements.txt, -# which is is automatically maintained by Dependabot, +# which is automatically maintained by Dependabot, # as configured in .github/dependabot.yaml. # It can also be updated by a command described in the # README.md file. From 9df00e38e228858aa372675af6809597bb3580e9 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 2 Aug 2022 15:33:17 +0000 Subject: [PATCH 462/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b2a0fcf534..f2060937e1 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,13 +8,13 @@ aiohttp==3.8.1 # via kubernetes-asyncio aiosignal==1.2.0 # via aiohttp -alembic==1.8.0 +alembic==1.8.1 # via jupyterhub async-generator==1.10 # via jupyterhub async-timeout==4.0.2 # via aiohttp -attrs==21.4.0 +attrs==22.1.0 # via # aiohttp # jsonschema @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.7.2 +jsonschema==4.9.0 # via # jupyter-telemetry # oauthenticator @@ -90,7 +90,7 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes-asyncio==23.6.0 +kubernetes-asyncio==24.2.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -200,12 +200,12 @@ traitlets==5.3.0 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.10 +urllib3==1.26.11 # via # jupyterhub-kubespawner # kubernetes-asyncio # requests -yarl==1.7.2 +yarl==1.8.1 # via aiohttp # The following packages are considered to be unsafe in a requirements file: From 6e85fa5ce869cf9b08ba07fe269224a11f646ed5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 2 Aug 2022 19:34:03 +0200 Subject: [PATCH 463/898] ci: remove dependabot bumping for hub image's req file --- .github/dependabot.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index a2b8cca601..334c2ae4f2 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -9,17 +9,6 @@ # version: 2 updates: - # Maintain Python dependencies for the jupyterhub/k8s-hub image - - package-ecosystem: pip - directory: "/images/hub" - schedule: - interval: daily - time: "05:00" - timezone: "Etc/UTC" - versioning-strategy: lockfile-only - labels: - - dependencies - # Maintain Python dependencies for the jupyterhub/k8s-singleuser-sample image - package-ecosystem: pip directory: "/images/singleuser-sample" From dba4b4afd5a730fe939f452446236ccfcc7b6bf7 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 08:17:39 +0200 Subject: [PATCH 464/898] Add example for --- .../kubernetes/microsoft/step-zero-azure.md | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index edbff73090..27099a1542 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -230,32 +230,41 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! -Optionally, enable autoscaling: - -where: - -- `--enable-cluster-autoscaler` enables autoscaling feature for your cluster -- `--min-count 1` is the minimum node count -- `--max-count 4` is the maximum node count - -You can also enable autoscaling feature later, with: - -``` -SP_POOLNAME=$(az aks nodepool list \ - --resource-group \ + Optionally, enable autoscaling: + + where: + + - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster + - `--min-count 1` is the minimum node count + - `--max-count 4` is the maximum node count + + You can also enable autoscaling feature later, with: + + ```bash + SP_POOLNAME=$(az aks nodepool list \ + --resource-group \ + --cluster-name \ + --query [0].name \ + --output tsv) + az aks nodepool update \ + --name $SP_POOLNAME \ --cluster-name \ - --query [0].name \ - --output tsv) -az aks nodepool update \ ---name $SP_POOLNAME \ ---cluster-name \ ---resource-group \ ---enable-cluster-autoscaler \ ---min-count 1 \ ---max-count 3 -``` - -or update the parameters with `az aks --update-cluster-autoscaler`. + --resource-group \ + --enable-cluster-autoscaler \ + --min-count 1 \ + --max-count 3 + ``` + + or update the parameters with `--update-cluster-autoscaler`. + + ```bash + az aks update \ + --name \ + --resource-group \ + --update-cluster-autoscaler \ + --min-count 1 \ + --max-count 5 + ``` 8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool for accessing the Kubernetes API from the commandline: From 9744169be441bc1344031a27e7e9ef84b3c4761e Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 08:19:19 +0200 Subject: [PATCH 465/898] Add Azure autoscaler docs link --- .../source/kubernetes/microsoft/step-zero-azure.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 27099a1542..e5ad3d368e 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -231,15 +231,15 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! Optionally, enable autoscaling: - + where: - + - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster - `--min-count 1` is the minimum node count - `--max-count 4` is the maximum node count - + You can also enable autoscaling feature later, with: - + ```bash SP_POOLNAME=$(az aks nodepool list \ --resource-group \ @@ -254,9 +254,9 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --min-count 1 \ --max-count 3 ``` - + or update the parameters with `--update-cluster-autoscaler`. - + ```bash az aks update \ --name \ @@ -266,6 +266,8 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --max-count 5 ``` + Read more about available options for the autoscaler [here](https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/aks/cluster-autoscaler.md). + 8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool for accessing the Kubernetes API from the commandline: From 83b61a86aadb7d4f61ab07eaef53ef31e3bc41ef Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 08:42:08 +0200 Subject: [PATCH 466/898] Add autoscaler flag `--vm-set-type VirtualMachineScaleSets` Originally `--enable-vmss`, changed to `--vm-set-type`, see [#927](927) 927: https://github.com/Azure/azure-cli-extensions/issues/927#issuecomment-534 --- docs/source/kubernetes/microsoft/step-zero-azure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index e5ad3d368e..2953362d81 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -234,6 +234,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta where: + - `--vm-set-type VirtualMachineScaleSets` deploys the cluster as a scale set. - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster - `--min-count 1` is the minimum node count - `--max-count 4` is the maximum node count From bc432627c98255d27162342dd5128cf57cd2995f Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 08:43:08 +0200 Subject: [PATCH 467/898] Use original values for `--min-count` and `--max-count` --- docs/source/kubernetes/microsoft/step-zero-azure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 2953362d81..fc95b158ed 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -236,8 +236,8 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta - `--vm-set-type VirtualMachineScaleSets` deploys the cluster as a scale set. - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster - - `--min-count 1` is the minimum node count - - `--max-count 4` is the maximum node count + - `--min-count 3` is the minimum node count + - `--max-count 6` is the maximum node count You can also enable autoscaling feature later, with: From db3844736b22cde3b99a6699ded3b2272b1638d8 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 09:24:31 +0200 Subject: [PATCH 468/898] Match original autoscaling docs --- docs/source/kubernetes/microsoft/step-zero-azure.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index fc95b158ed..bf61f35483 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -230,9 +230,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta This should take a few minutes and provide you with a working Kubernetes cluster! - Optionally, enable autoscaling: - - where: + Optionally, prepare autoscaling, where: - `--vm-set-type VirtualMachineScaleSets` deploys the cluster as a scale set. - `--enable-cluster-autoscaler` enables autoscaling feature for your cluster @@ -263,10 +261,13 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --name \ --resource-group \ --update-cluster-autoscaler \ - --min-count 1 \ - --max-count 5 + --min-count \ + --max-count \ + --output table ``` + **Both** `--min-count` and `--max-count` must be defined. + Read more about available options for the autoscaler [here](https://github.com/MicrosoftDocs/azure-docs/blob/main/articles/aks/cluster-autoscaler.md). 8. If you're using the Azure CLI locally, install [kubectl](https://kubernetes.io/docs/reference/kubectl/), a tool From e2e992084fee95d4cc2665e18bcb021fd4d05b56 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 09:25:16 +0200 Subject: [PATCH 469/898] Migrate autoscaling azure interface actions into main --- .../kubernetes/microsoft/step-zero-azure.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index bf61f35483..4092e94df2 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -310,6 +310,47 @@ If you create the cluster using the Azure Portal you must enable RBAC. RBAC is enabled by default when using the command line tools. ``` +11. Enabling Autoscaling (Optional) + + If your cluster is prepared for autoscaling (`--enable-cluster-autoscaler`), + move to the Azure Portal to enable autoscaling and set rules to manage the Cluster Autoscaler. + + Navigate to your active subscription on the [Portal](https://portal.azure.com/). + + Under "Resources", select the VMSS. + It should be named something like `aks-nodepool1--vmss`. + + ```{image} ../../_static/images/azure/select_vmss.png + :align: center + ``` + + From the left-hand menu, select "Scaling". + Click the blue "Custom autoscale" button and an autogenerated form for a scale condition will appear. + We will add two new rules to this condition: + + - Increase the instance count by 1 when the average CPU usage over 10 minutes is greater than 70% + - Decrease the instance count by 1 when the average CPU usage over 10 minutes is less than 5% + + ```{image} ../../_static/images/azure/scale_condition.png + :align: center + ``` + + Make sure the "Scale based on metric" option is selected and click "+ Add new rule", another autogenerated form will appear. + This will be pre-filled with the required settings to fulfill our first rule, so save it by clicking "Update" and click "+ Add new rule" again. + + ```{image} ../../_static/images/azure/scale_out.png + :align: center + ``` + + The second form needs to be edited for the second rule to decrease the instance count by 1 when the average CPU usage over 10 minutes is less than 5%. + Save this rule and then save the overall scale condition, the cluster will be updated automatically. + + ```{image} ../../_static/images/azure/scale_in.png + :align: center + ``` + + This form can also be used to change `--node-count`/`--min-count`/`--max-count` that was set previously by using the "Instance limits" section of the scale condition ("Default", "Minimum" and "Maximum" respectively). + Congrats. Now that you have your Kubernetes cluster running, it's time to begin {ref}`setup-helm`. From 456836fddecd48629fb8ee38e588fe858533d3cd Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 09:38:21 +0200 Subject: [PATCH 470/898] Match autoscaling docs --- docs/source/kubernetes/microsoft/step-zero-azure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 4092e94df2..0269a0ea18 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -250,8 +250,8 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --cluster-name \ --resource-group \ --enable-cluster-autoscaler \ - --min-count 1 \ - --max-count 3 + --min-count \ + --max-count 3 ``` or update the parameters with `--update-cluster-autoscaler`. From 471d0a1099c22d147ab1b7e9e7ab3131d919d065 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 09:38:42 +0200 Subject: [PATCH 471/898] Update Azure autoscaling screenshots to 2022 --- .../_static/images/azure/scale_condition.png | Bin 322300 -> 108529 bytes docs/source/_static/images/azure/scale_in.png | Bin 114727 -> 39785 bytes .../source/_static/images/azure/scale_out.png | Bin 113178 -> 38193 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/_static/images/azure/scale_condition.png b/docs/source/_static/images/azure/scale_condition.png index e5225baa79f3ff8fc9c0d775e36b5275f89f4ecb..d0cb3a3809ae7cc008d40abc9f043b4b442c0244 100644 GIT binary patch literal 108529 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 literal 322300 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 diff --git a/docs/source/_static/images/azure/scale_in.png b/docs/source/_static/images/azure/scale_in.png index a1aa0388fef681f4d0b582061377c8422c4a481a..89198cd7f6fa6e04603f77e3c54aa262040cd9d5 100644 GIT binary patch literal 39785 zcmce;Wl$Xb*CyOJf#B{C+}+)SyL$+3!GjYtgy0Ur-Q7J9+}$C;ouI+D$^CzHcdPcP zx9a`UH4M|!-9I_!TyiE%Sy37Z0UrSX03?~W5~=_IF$4fm-0+a#J8X8LW&l76$ViB) zfBbf|>Yj}4r~0rpL#5SUeO>vXwQ<46M_14uv5HhIJmKO}(WBvt2y|8&b@DVj?~aWO4sKI5 zCrP40lLnz$TOTFb17h&?3dx3OVxQ6er$_634VbBjG01FxX3R6F@A-_gR6%d^WwPIM zAd{dw73JmY1x_GN9ksh9U$?$x7(_4h)vn9Uhiu`Xbtx?}LypkJ!tORmqCg9yO!2yt zIPJUfx{RIV=jl@AB>tsPxVI zdoi)t6I9ADGGQ|`Xi;U#(mXUn#OvW#VlwYTQ5IZM+{y-E?zf>uQG+aqN z-r4@olMN4$20ls{)Ff%ScuvtLGFvU+M7ew;5*Wh<{C9bCi+RJf}yQwJ(o_sFnYv7d=MX$@`E(&OhUm%A4x<3;Pq|l*a3D0}S&1 z0vGT+3lIP%))0h2K)~QIC<4mJC?J#rCU9Uj!mE(q&{|MIyxOEgD=ezd0Yh#mp{-7cE$5rNgJL{ z6Nz}RRbu)B9#5{L*2Q};0QSO-V|ue31DUB+c9o72YQ09wG(Ew`Y%iT~>^@-^UB3L@ zO#I`VzCumGOztskPw0xDh4-&uye&Re+A7ZQ=vBS?Ht*Ae3qHEFUPfF6pT(>%<0qsx z$?)^RjL+`J0N$WylhT+s1TVDt_R{Vjr)1vdf+Q8Y#D-Mcu4yA2gl_qEkFWY{}cA@RjP7cmkgOw{|~I47CK8d+rU2THieqw`ctVh4d?;S@@ zVm?|lQdXj2rG5v8$u9~uH8r1nw0Cb^U9Ua`ee~oo<;cO#BLY9c%i}-!RX1p?tH)A; zF<<5#O8@^QI$afN&H4=fjJur?P6*zY{*jPBwUzs82$m$FEU&xu(1t+AIp8jlN}`Hx zoVw6v)Hc+m)lvL3pY3cWd@Ax2)#TPqD)9w0|8p-{SO?zP9;L<&rA#%eV>K&&WfFY~ zryjiKc5~0m-@gyARAGwn_h2~26m^n`aR@MQ2;w|yX%)fA_T2s=mOXJBFP7cp862ZC zb)ZzqfLO5|r#F+{%b1 zO~3?_vhqh_dxv6(mDZB5hx?pAk2ki0m(Ud`3W=_cD_ zbQ5ylhhTkBEkNHZ;ftkA3SHMpAIdj$3G60M3_wIDxkH$${*U9a=;AG2QtQ`L^vi30 zvEaPXYvQA$qp@$i1C09u;II?yK%DqwtEYP3-I)S= zBUiHn!vOd#yl;FKvdhY1tMDGiY<{rjcmZ7Efh+fr-DASlN-ik=k_)e2ogcx0pOjH- zza{MImF4lZRFJ?Ht|-<4pYt;3$AZcVeac`|Yw~)FN`bSqCxRw+K^%kvag24z=tNe` z<4oj}=P?s>>AAO)_3zCVO%-Y};sEQ{uD!3;KCoTwVE8|>Hp0UFJ|R+M^nHC4=_A

V68tX!QNF?h_f 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 AOaK4? literal 114727 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 diff --git a/docs/source/_static/images/azure/scale_out.png b/docs/source/_static/images/azure/scale_out.png index 0d789b1ce0258488dd9c0394e2a894c1708e14ee..a80d756389a4dbf3f956d300b31a399c5efce0e5 100644 GIT binary patch literal 38193 zcma&Oby$>b*DgMaf`Wj6fPjFsw6wI!(A^EvF?2T~($d}1-7yR?NH<7#mvl2U`{sF` z_uaqm{e9ow`#AVxjvMY;b6snl>pagzkerMtIw~P52n0eG7XvGRK#zWdKu8~+JqC`P zKq(_Zptm4#@Fyjgl-+q(HJr_JltVV3y5&~t;JTM36QN_2fkf|6gC&YoEZyfVcbtmk zYHDw5pemM43$=7g#n9Rr;gU0zN1r}v{d#Ik6NEfj_?GTl)Vqls39$`C*RPkAOi2!pIV=SUOvNS0)71$Ve<&| zN(A$@kS|E_m+vUfgXYFpS98tzFs1{i~?OLTCJ^N$C^IuXj9`Eo&o9Y90drS8g zKuU1~SXREa&2-A!O3Et@larGwft&3CrOFe7r5Q4MYCZ8s<~b6nl{vTD1Fs+bsw&)- zMEx;5I+_|N2J1{F)i2MUy_lJrM&%atn8@=>w(1+s^Fv|gYqY9oFkN5n{w>F}WnV&2T|xT2%f@{TWhr;w zcQc{52}QmA#XSwa<4w9JuU{$+Yt+W9I*W44C_Y`Uq(jOa{0tw{q6gGB`5j%`tJA^L zN*cK56+9(`Nx)4#j-zs>d)jZ-t_4{xw6`w%qD$(i4t-{OL)hb6%bMmePSmQh-{5q{|CCxBo62hG7>mbO z2F%owlh>yeP>>rqxoPxp5~zxYq+<^y-%M9*ZeA+^i_LRo*PJO-<5YoXfBeTqn2O3ll8I$1rcy}#5pmU!yO_KanY?irH;61q+iHd=>ev`0oR5Bngtn2eK(&2pf zg;h0c`Jc~3d9Gz&s|oD(6_Owa=O_X{gR;;>$)B!d`)Q%v-MPknYo}^ZO%;8%jmj2jf|Sx{9(#CMB$`4HY>`R>b| zX3%(`UV7xE&6(LU#rYFTh6wD)Pej)}Wl`36Vvq(!5D541#m%A5HER1;mR$)$HIPdj zKO2P>=&Q**+j}5axCs1}7M?uE2Yq~dt`eV{#^ZW7r|#46_}A776W?VvMQk zYccB1bHoj-ib5o;5(@QV{y0GU?%fm?kpbaiph&)O+#_{dPG;J-@C+3-bUxsexb0AW zt+xLr8%3Z}YOQ*n?F$jyr2F@;H5E3#k#5yd*2!@m;!xNcusjQeaM!LynYXjL(%?if zZd@YmlxkJD=1aheG8qhhYA9X9I73}kVr*^VXC`)?av_8VMXOolSy|)*Y0R67sPW}e z3qkXd!}5_VR77j4OPcSsTXu&DSfhcz0DcrpRl0;qqM1?z%O-b97IG$cm{ZE830CML zYzRbyPF9I-U@(VOd3)KexDh>}wXocxSQ4Rc6_82qHITR^qP3n-0^L!?esUv;&4eLK z%YH)&_*^rRQ~y>py4*8)WZTBKFRh}$`GN|6)J_Ba&GpsP$FA9~F=6`z;6begZQeC{ zO4~0o+MFg-G-+sXO;q|iUfC0DaQ6fEBcW^C;ZO; zbD5V_7fy*r4Uf}qz7hNO*_A<8SgrYZp$J09$C~ebv4Li|sA{P~7lvVD+2Bo4PBn<4EiZ)uS?8V-qnLzY$1N7j;??j_*PLCnK7}fcm!Z0;Mv@6wCMYk<4ptDM z(;*R)eZiV|@lFyqPb~!y3)6G~&klVSs2uP`G&t=gaVK?sygU+(<#m%0?iu|mk zx!|?U>pY$!WQ|75Llo|}$5;N)@q{Zm^ac-E2yxN)rvz)jU6KReP$C7K+4S(%ND7sZ z%r_o)vsTwR8V;(&9coAfG%Zl9%B94{^p%G-`G;{BQq!mtz&@}$QIqQ@HjZUsYB<@} z>B%o;%d>SUsMKBKb5~Q7S3)N59fukf8&D;o6^P@s4oI}-GaJ-4!lpcTci`>8tQZ~ZuITaHY_53> z8m6?woEp6)(HRtp>%IooiaQks*Cx~lQ%+geQx!=2MToS)pBUJk?+8AKeAe2BOE5e3 zf2cnAlviN@dt=gAW^8Z24J_t*NM}n)Qs40%ad5JS1AW=aAC_Bt_UDAk3zDc3{8v9m z6Do*Gd?dL@#F4|+@7N8g)9WT(PZKg7ENT&%KiseTSvz_7)@5bPj8wS1**S4lz6laB2=>ABmDVc4&@SmkMP4?q6d_V>%#9C>U^c|v~; z+lew&=mRyXZE#FCvk%8d5J>y!znwgMz+SOd#UZoeYEhCY;RX^tI^2*OKug z{w7r+e50E1LtI~#dsu!=OVnwUq(fUtyN9d8(T+VUncX#o-H!b06X?*Jz617RoM;c< zz`)&^v-kzsz%Vz%&BE8THII_*FcgY zT$#(>_GNUGV>`1HYldKOana#0(uQM#^CZqT~}j6_4t;ik=;X@kbQ>)hkHnVBtDi$G0-HSSb~cs^joSzF7hL?{wGEjy3FMOFTuK`;6!H)Q z%~P+APpmcxG2IzWF(IY?F`{7S`^*h(;(|lVH`*|h%69pc?Vn_sbJyV{#*`u^FCP-a z??{}sdhxX|FlAJ=nG=(&h9?slbQqRT~n9)D`BG zdqNvY%U)YnbamP^D3oK^&7igEAwh|7`_e5H#t#OzBb%mAVEt9tUa$6ellA&-PJW`) z1kbXZ5@n>2uRlgfs-#0fK(=&~Jpt^;YHG8F8#m58^zvZ`hdFYzcIevz*$MdOW*S{j z=H|3U5jq@&t%8{@u+X-JD#Bd(=NgW)PKh{UGiRH4h5x9#kf)5v)}m59p(&3M+A#`jdINgO1ssY-c5r^srL9 zSzRZqx-;;(1?e=R;xjzKGT?z^ZuYp-<&#&Eu3N&(mi=|Miy5Kx+#LE$I?a+ygY6qe zf2OGSdT7hmq7>|8B%?_*^a*P%cKRL1mzjn;s*A#FUJ>U|l4rIzy_!mTljLq!_ELIV zQ`6_*vd?nPYUbjH-wu*9cPRx{qEpX1TS$`4-YL>`{B^@i0!zV{qf5>tOhr{#gX?o> zn?q=EmIc_)RVmFz-zwAY4=+xj{*YgpU_X{q3Nu`K5A3!nq(}lVIw3c^BaMD4;Z5pm z;LdiYaxKFRMHIxe=DqNzEF$9fX!5uvlB@m+CoDbXwxyGq@rw6C++H$~$CgtJ^Wb z+E3}UE?Go^bfIfg`C@d9U!<9cJM9iGV3_j$-Nfqho4&K;SwVH6{cQ#oO&@Q2TJxzE z>$+G)xnrc~iR-ATO&a?Ph@Y!dtZop(8yUp-(YgRyD^5tqh^h{jUHAmOfW^((=CDC8 zPbr9lf``uTc+B}iaL1UvQfi?<+b{ij+sOdK+oH#U9w4A6rPh0#|H9w#l)(6;mGG%Y2D<5tFZ@5&wVKMqt7x&Sbeah%9~f|=G~Z^Bd5LW*M_`R7f&(~i|SO(vFkr20n{ zzcXuE5a?q~8^^yp+P|Ue|E>9r;aV;Y3zX|NLWNm4g2ACq&p=DA$_rZrLn+)%jI6H?99Yx5a^vE(9qO*6e<;5w)ZZI6g~yzN7!jAyeAr0 z!Cxml7`nUT*7Mm@$eDOuU15(xg)v*@R_8uVj|3VcHqugf=lNRm3KiMX!t{64DuX0g z+7nN>gxILaJWqhpqnl8imL3G+Lowfm=y4xqc;4dpt}4w@1vXW&)zU#sRY9O3|6a3`vx5N9p9F@Duin{twBXQ>EcaT(ieoe$8WT% zOB4Shu5W@aD9~<22v+tJT{B~uQ?>Ny&PKG~&ie9~ItvWOlEEC1%B(S1!fm6{KQFz==LKWHug%(=1wtomi-^@DNKy)U9-6*38_6Hp+*fNR0BcYv;ZfESPaiz- z1eBlLD^nMAeV&t@Z3Bf_5PkSyv(c}fV@(A)&~t>%w*}#dHcZU~Q)&0hHaBrJ9qN}> z-#>A5!*h zg0p3Y$SV`oB%xOI7TUvmxdKrQh9{60K)hSfFzunf0!4ERAkY&9r&iCi@ktT^j|*>7 z@3Ehs+U$&HH<}*v49fB~e>6WU{JizrpqY7C&ynuNG$ioUY#Gvb#Q4--%fr|7GRn^Lah&7c zJLlzZRP)Q8qP?RmpsyNA3$RxUuAB3yHAM-jgnI;pTBjk;p+`j6coClTwvq5=83_bR zpfmWcDI~ZJbbOCuIf2IStNHg}%>M_%=wI7wd}*ZvEuKlIcuxoPQ9D34NgWC~=56D; z)4JjbCly?PI?GyFl@=AzW0}=xkP`~(>1T#QXorP4?jIkgD&_3Ab~NBKIlkc^*)|6*AEt&Y z;MLk*YdL#_R;D}jLk*YY>W*(s3si1s|Gh7Y$!*Y|X>p*eopPl{2F7!GJ|zL?(+Ue~ zdV}LmqrA0I^5=tjcFA|5D*k%=c&1cdXR^$lNw%QCzD9gzojRB0*8E9PRX754m_m2J zp|!sKTYK8@cjurcZta}r0#tEIeS`rKM;|P)ry!3$uQM;?WE4MFn_tTW&HY5gt4XHX zZG^?EFQAb@Mjq!dyFvHLPW_9E)L2US3HYk|(6y-5n0^fsn=OmB$+?Ltp;2nVur0nQ zqqi9ZY)q3II~Tq3-BHt20mhWsVC(Oi0aV&!DXmv6#<-7u= zs23-vtMNsS+~jx0d34I=R?@&@HjAN#Dbgx$dvV3(Vqp#MT~KY`xyRBue*V(g+vKO2 z3jcj4nPcw%xym^7JN*jp`G)_=gbH5dX`524LQWV)b6jzDTnm;Ud=FvNnA9Bgg;Kgb zd6zm?qd788DFR zPe+C4o~eqoM~LpYU5A_CV6CT#t%%f-?9r|#ga!VXi4nS%VDGDw;5olZAzDuR(BM|r z5F2~YZs~BLR-ja{+I(6y!_}!gvXTcb{gp(=eO!i60pjPyRG~PBnLAyDPpo-s3?Dy|NDDWKVGdCoqcA?HO*Ce9{7F(mO z0mYHv#1|2g@)@y^*43_X;6y>Mn>yE%4RE_BnX}k$IS-l_pyEQm??qax{HrubSR7~S z?KzU38ZHO4L_ysGi_XLF@f`2eqR;ebt3VP8CFEee{R}YNR5v3p0M;r&5@5mK{wDw^ z?#B}WqhRjJ@;hRu=RE?c>A&u5$P2&!^$BRTfWB_FWeTUyYTPX?`)SN0KhBehukta9iPse>k6-JW z>|RWFJPk^Qbq@5#KLR~#11<)QXSmEHL?~2zcc<~X&5f1>HV9A?NoU>U3b?aH^!=XlLe&~)W+B?v4giY=;OpH7p*1Cb;g>dlF9J-NTCVR|t|bD- z$&<=#JvI_=T4!aYMScW&n>{J=!*skbwsp3Fnh>gUq<1-ae|fknYp(Adr#2!yNSv9R zos#p?H~<7vR)$fk`(Rm}tsL*F_K1+%5jB%Nn#KdAIoHJ55ZJk{YOWL2)VPB{Ez5aL zKy|H1vmx*w-Z^{h{1mj5_;YfHz`vl~A3(S+h*Nc3I5sXR2*hBEOG;NoR!Il?`rZcp zfrtIiwfx^IIsYom|8otWW8F=`4p*x;N&!pQvT-|$Ai4XU{^E+(P@2b=6G7pnR+qAb#pHl@>*quN`@y*`y%fX zQNyx+`UIznN-4&CthRnoU^b~%!eWg`!!tPyP8=)#p)Koi-Jz$zv(i5Gy$P1M_%qrH zWURjktCMf`*)XU$@dBu~ZrxlR!aMDc_A!hct#|iDJtq z``q1F<_kX>eT-qn#FsI%x`pIR$rThBNJZ>i59{s;!c0BtTlQ;t5VZbX*%yl($KjVZjWn9#4~9wC3hfrR?-<5a9j!Krtn z-!b-8a`U#WRMtdRsiqmIBe_$1Y&a43RWcN5)f~U+!&~e0sJnU5S1F>>aOQc5C}7a6 z{@ra3_50qw$ZFmEmdiM7GUYJkmAhVtn{dLO*vhR*d_M1+Hix zGFCApU!{KAdEp&z6wjc!%3qh5qt)bZG`)I%chxe$P=tc|T@TMEx25MPaI>c{7D#4w zu{hrvr1i$cYOxb44#z$!#>>PdYX`FR_YF zBTKq3^TO2y*#?3aruYq?1fEu~nylZQ<#`Zviwme*n|+g|Mw(k+84PD&lWRB6k|2qJ z*|#({((CO3bZe~XWFfNJ;?0_sk~w@Sog^h|_6ro5I_X|xQ(4L|-PWHX?~D5a6Z@@) z>v@lMI_^4Diz-WrwYjuAQx{XW)QvUI2v#;Nj!jf}F3)W%M(Qi9yPwcv!K!0z&?Gvg zY!#U-spill-LJo$GQ?Gmgx_kS~6}Xy$?xLm{>!8k_>Nqs{byY6^>`T ziTj&LUZ2Ml_iv#}tQ(z9xD?KB4Ij|GVeN?M+et@hZ#MTrpNz8S)p*SSyltOTyu9#K zCDPn_E&ML%4ksI|^j1bAKZ1U(5@>$`8SB5`jm#+R-YQe{ZH9+VC*P|9+jj!5B}9vp zR;mmNWbHW)tv)=BC(Kb)m=%rw5IDt20&R(87M` z_h{Il!g0JZw>W}DI5j8;;M;wD|WAcuLE>)>Br0Am%k9BiZFAKB_nl^2gR zRLp&P<#TgYR!Uiqa_}x(TesiB8=vWtb)3r5T~@E4rhe(IAgrl^@F~8&$Mu8_GK;V$(y+5I1PfmXUd(0!Qlwua)V8bBHKpbTw>m$ zSBvv^HL?W?=?a!Em8D6tpMX_19$g149=L-xG?nfztO_%c_?N<;seg~WHla~rI=opM zC+KqVQ^Z-@=X+m-WV7C6R4f?4v%4GizHSZp81k3chIjWjmZNh}u2e17Vs|kQnWH`q zQurMBD(-2!OU{@=)!am}z__B3tva1o`4@G;fnrpZ#EZyR%~`DkE-+Rr;c)ymJ#!bUc#A!^8Tm z2fKpt_<6o5GhkJ)2uEWpDm1|x&%3{*>6U% z1^G5#p6ONArG)xW`HH#9Ro_EaRz^0Zh;H-NPzB6Z! zBpsq-Bx0`)hcQqjOD_%kl=c&|BKj7teW)I~#DBG4Stv+2@$G-a>_6;PdD5K?3vN17 z#IxM2G8Pqfgjn=MF=LSM*&P0Qv_B*n&$KrO%hIT1#3HDn6-Awaz8y&7i1K{U)=Au} zYz|$nK3_A(780NiK)qB==#?d6dGvgg((wj`2P>*Z%kE0aSTRW|CL1r9Z*$8OX&54c z#jf-UgB$48#YINIjXhQh1>x!1S`l@Lfj=hB`}AI{UeCT0)}tI~xp#9TH#YY*8eF$h zRs9&3ssDTupfmVAHIt_$YfBQ*u3OZ&7Se6PL7JyKz>)(5rCN zznvGW@HE7|q3ibgK+0bt&Iku~bzUXgtgS?uNrzvi`!OWm|-d^G?{MSUQJ7ZoC|B(d+OY+BjM`6^n2QIVK- zX5`6Pql^LTi&YFy=ir8l7Gn*m&;&<ZTGPQIM>BKUB`ie-V2C#^<5OtwYoI%Shjd{tll!cD#K=_Wi+*P;R3ZYI^s|I zNdsPN-l|9vjRbu8ixw557v0@O%ik>|69^+ld9bv6CQX9)J||sWF==f;Y1=F3E-DA9 zpIOtEdHW_Q7ybAV^5Z4D{i2pO@q7!(9Q?@Y-a-1G^~uwTZ3ReI(Pf zNK_{N6eEt2a(i+J3b6+2>3S$&T^JTvgbuwu$@o64GecE*tBTKDF;^>mNIpGB7Mvq; z?Xf6)tpa_A5HiJT>^3U>%rY+q9-S9d0S5rh=2Lj1Mi#$trP3J|M#H$v2@(_&0dhtkc$DOuHLU?R!Euit(j|;bz;Iolt#m0 zbhExK8Y5hJ4f^=H&i}u9ul|kLzi127%K^wXAn}!7L8leiA64E9s1*8isEdHJj2<8g zoANBys4WVE%qRggX&oN=&InFJVMcD8Bg+KgftsL+)PcVE0Z6-$?#P#HqYr@I0Lv8T z-goVU%B!h_VPGjw_PSIcS^X*cD65iI!7qTTBxru4^O|9GyF*Roi=O0!!g4L0(WIe# zhp&lxh7gR0-NG@@jKU}`sDKG*Kjc{a?-gtcx8+{3Whv?dAJ!H+avh%qNzga9ni0G? zTAlUM?JSB9b*j+t@vD%R?3gGmQ961{Ua64byOTCyaP*MJnAHc{V$>58wjWGrxT9a( zzNnH%y1OqD_{@zRl$2)2=O-97eDlvypev8@o>VDxc^N5QfeHVNGRa@6_CuW;bCftS z7I>Zn#up&-lw~Y|Z@5rYE+Os?vo2V9w$lTx9q^`Flm!k+w zd3K_qG3Yk+H1WM-&EXpEFap(~M!^O4b2|*23?>m}hu< z&$3f=freSPUWon)t=3S+Q`VLe+(W($XRCuRMcc0Ytyt1|-qHwEQ|HEbV$IDXGQu4sEOIaH zm>7#0xw)h*i8oQhZT3iH=CT#&$9o`~wXzAEMklW1G*=`P3eZ}#J2LKBx+dd;)~SDFV4NfcTd~Jl5orV zU)&q@2;LaB8C&q!THMBf)DP~^qACG{47YgS0x_4B)Dmwf)@ znJ(zhQO@G$&QKyQ2Aexs{1aGPgT(+Q&XosY$+i9L7;x`Uf-&!NM3Tn{)tyPtP$DhX zNtY$BbTJF$U{VpK+i2nTar0c1UacPPwJyr4qT#`+pmO4aP0+gt^UXH@+|WE9D|fDM zWy*!zzG=c)xuLg?gFlhzId?NI&-EEHQB>*cz(BRE{ol?HSNl{2S6pDcah=>-T~>T- z*o54Fv#e%Sa#|%ZX_L(9Fn*^@PF1m@*y)sR!TsuXV$@nS@5(|UX%JAWG zZwa0BLfn>V+&incHmw**Blq9l zA{aHDZrRU<#-FR!mFg4=nvQ26^)*SBa5F{oTJK};Hp(<>+e^+u zt`X<>=ik70i3$?QVImAlU0=*Z7B7y`id1->tbxyE0+pb}#VKgl?ddDZr zfkn5ezhb?Ie92Rf{N(%xzinFJp9;I%=Ap2wMcT7{>j2mq78U`T%pJSnZ3+4|PJjxg zlcyT}ZSjZ}>J0BBk6Vn3{`)I0?fNkTFg7#khPX8?maB_f5wl?hSlx_(Mt~H7f+f!j zLm-)poHg+_uYCkd6Tf!Lq=9}#Am48mWqgvR_NZ{tFKb1fZrq*2fLBg{!6oijwc;*6 zX~;9Jv?;-3)j<%Sj%V?KZ`_*kvF~S`uLYdmX+?N5IrVJwYpnVfB__7P^1e`{tIo_s zF0ZRzT~+XF1(Au*A_q-Y_FT%uo&QNHz?pm*IraXAByx}Z?GOj=h*p0$4JOkjp|uv@ zX#JKLIk|bZOHK(fmN*anseB?&t+o?h^2J_1+niI`B49`3>sFnJ34C|ra>>6pMp#!FPXRS^zF^7MSlNyXZ5?fhlRB=IHodfXEG#2)mwzqapU#o5G*eN}q$Z zL|@!w0*JcQwlaE|><|-0k@&^>#$dp$vzH8bY-D+jNJjv3-_Vid{qTI4S_4Mf9)WgH zv~roJ=yUCSzfu*>Wy0e!`pE#xeIo3gPfF(vg8thamg`3odCkr8O2p#n#_+KGPn))q zUY@HH6A?Z{Yw@|Stp5C2xY?#Y8-UJfjt(f&s#{WXGO6AtJ@KkL{I!$5$L8itMORm8 zW?Rj^0)-}-+;BSnNgaU}PnL@db##+9KQpyZE#>Dgo4Z*&)g?Ul^zIS7<=Z9InY*%d zsiJLe2FD1ULXTS=eXDE4>chgh&>YFvYSiszVL@LVKp|V&M#5}7*=tTxh z;cw19-Hwn~`4IEhr^|xzl|}QDV@a@Khqv-tg^`s8L&438IOmSH9*Z;bgJAzIaWvd( zS>{960=?+!)V#{YQ`3xQH^x-U&)`r(t`E`q@XI*&otx9X8v6ohYy7Jhq29@q@vaC` zID4*m_x+vhhTJ(dxLs~joZg>Q6{^m#b6fM2pM#3jA`_fR2 zI}z1N$gB7Ej=pDQ2vT0#xfm|4#0d&4j_qL+ixHHvu;8 z+gsM?j#rIohuwH zN7i?nZ)OWs>Vv0Sf9eBz;v)P#qn=N7E$`#Q-4^BnfHnDs^&nt8n0^0Gg8_eo(}_)m zx*|q{)<=MZ@uR9pQ~KZ^_XZPSyLk3*0Q=9Jfmp1>?l1e@C7)Y>U74sEQX=p_+TU6g zw22sZ-!V?l*p?7H7y>Aha;CGf9>k{STfF$HR{xKuZnuSpv9)O~j#Wc)jydTFlV?n{ zUmY3xCR+Nb@IoXBeoX*Y_VHM+g>Eq4AwLfVa`{udA76vTqxr+Nr2USiTkrhX_DxZd%$<#%togRfmdF;f!FK78ZTV zC)XQv*H*0f4%x(Asztny=a;=dGP?FruyXxX{na-aO-S9H&+|*fn(;98*{sy%pPF0x zOvl{4wZF(sKdnym?|`}cM8$7Fr5j~1xMv_Vu_OYqt@D9t1DP7oM6GFlFr*2Ug}z=c zIjr^AxBW*T8hO+I%mWfUbas~W!$f^V*dGtW1-MR|iYdTH?K47O2;Maj3Mz1-Dfx0b z)qWln{g8mQ+c?Pq;LwLAl5xI}lEMrl&+|Mt(+1{=)(lWlLpT8LJX82pwD)?n1R(Gf zb{D@8mNEKL#L%75j{C2idS=|lON_@&o;SlH;7WxGwbh&cBhc0OKh(6bbZ5(1#2DmG zJ0MSu4+#C9+oCHD^1e+HXLo?(D4cP?0`8VhG?teG3^N>A4>Pc=vD&#UyMGvH`4RD4 z0ZUv^N*NHV{~SVkas(8rJ`V6luAOB`6eutg?I~!7uW&2wZQkziJ2!5ovfYY;jU~-* z-O+IGa@512cy~udopQ`ugAqN0!e|5&08?u$U(Hl~=T_nUL_TVZ8sH2x=9Z&zlPgW3n;RQQ^Ok|290875 zv!nuCrgqONvO`jM*NO}&fOa^S!Y_0nu>bK{IvO6VU-^`nOYasS4OCVsT;WOwuJ<3h z_f~&mvjw^j@x~J_lmw?1>)_tSed`{FPS&)Kql|i9$8>uF!uL8#Jj1>H@8pSZBLGoz z!?Xj{I-(?}Fq_y%UlOS^*zKK4{fo^23_E~e4^TlW)lj*;LeKAg%bmPs$e4NCgS8A> z0JUSKfGaW_3=loD@WQv`1-j7N9@`-F+;BVUdK~ERvOx~u1-Ks>vKZJ&2SpLD|EBWP4 zcR6VCUpNZ!tp><{At6krN%X(IH`o8(=W5v=s8XUq+t^eDRo@?-|F<0YEq6w%xvTCg z`fcf8Js7+;7lw!2hlhQ(vpMVX<%q|sWi+J`g+8wG?E3Pm7VT}wN8(r0o zRzJniQxQDDPs=xw%i_kJ@Qdkl3><5L6czgc77R0iWYQ@6kP7^h2h(J&Us&qQYQGNv zn%g0oDrUy9r($(qh}+(~HI0W0LZWrbOkQ_RH2v=!MrizEO3UDS>G+Ub8Ylj+Vn7TR_YiC>K02{UpV#|S3Orw z$GbDDE{zG$G6OVPK>sVO>L8SZfkyNEFaawW7niAlphiZ)-Qf7^{cN=ItfauAUM%Wp zVE%_f>#x)iXXA8cnbJucuQz<-wX@Oxhf4(Ry6kT3N( zxPAkQ29imK){lSGJSt9r${*6i<SK)Ja8C>4$bZM4o?!7AF?;pTd_a&1| zQHIAs!@wgjwmYnzW@R;*YmHX7nk$QjspH}@f7%+Yk7w~0S&$;gkegu#eS^}@9(+;N zhG_;vQQnA{uwcH4eEuSQnIvQ#`>xx5&mPk!oTsz}@E2sN1nl=xSJm~>UcP$IEA7#p zSegYDv~aUyM0AtlX%10T1#(y}vcI}yNWmdBcU`;myV2v?Yc%7HlQ+b(Fb+LQG*^=k zNT&-ir~%dmTJgj&N4W_wN4ISCeQ2(f4Mxw)w05lojK{4P#%`){>f&%ZToUY;jvc&O z%&WXiDwoZyR)I=~J_~AAZbZA&DW%=l86BWdNnxp+u_WuzOyp?onf!hUT*_ejVImKC z@!8@mFj$8fpAM|Id0uGtUy|pWQlovJ*XovQyRn-RoR!oPka}G!W@GJ@31~g{BuW+X z<+`yH=~HK4LBboMTEmH?fAmpdn(pd0K;0xuMU~Z052GJSw17C_)maAMIo{!f2-xo= z?}wnh_-i7s(I#(^wocR%ck-?GZ>ex>?>5${iv}souo5JF*KOFZRm@jVEtt8{6qJ6b z!eVgn{r4Hf5}wVHIVD5{QJR!|lV)ZJQrQKj?HGG^pTAeo94TU%Ig76Dj6!0NOuaivXf z;?q6FW%w;7P-{jau-iOc8ez1F`_h5xmLxL&OKbZu6L@WTdU899ZSTBEAuOrp%U=Z| zI@9!xAqibAM&S4_oXD}V*}E)fcF|LxRh@b#u^e7EVFlwQ^?g4<^}ifppA9X6??$~> zLhx>u@*zs-_a3WfnjPW8K&2epdLeISYqZX;zIs3YYJaV^rdCw3HchlP?QyPV;(4&3 zbZ%Xw**_|W6h??TurVB(4a*aAv34Xn&9^Yszu?N$Ih9h}%QLf_gB7lQ{4iDm7lixCg@*{kn89Qyw`H0fGh3cYv1Efuk4 zG4Gd=rH2fB%gm#?|M-C<%`>k|(O#RiDW!{FIC>SENQ!h_%7MlztF22S=Rrr%-SRBA zC^xeCu8_0uI(FsA6@DJ`Ko&7OiIGbt2~(IA-O5*bV$LcbpN&yBTt&8O+Bz8G|`hg!$ddL!?r;zp~oDoJp^ zRY3|5%)X1tW(?Z9%^7fi%E#SE)=+XOso{Dxl+KXFx&B*VM>3dKh-A-Qb}pOlg}-|6 zN|!0+fX^92ETQT;x3n<>LX8B7xJqIgby_4l1cCfcd0Vj%-C#Em2)QdJV`ew(A>@(A z4sbh6n7`5nL{A4Pahw;Wso*U+w)=qQ%pvi9ltj#RnmTGFBI#yu0Q$ETt_mF{$qAUO zZ)wDYDO34*eThsH;A#MT3+=&JDLZ?=c6_z@=%@@&HzE2;vLA5$w=%jUjaIe*r0=ir zEgdS7+OZ+XR(X^v9JL+jbnSr2+UZ5X-8jkXQKtq}TSO+_Ccw6>Ltu^vh;jBn1W=PJ zUT|7DOe+fHw4BNhQVCHFoj(F>^jZR%z`PLieZ?&ey?+#a zo^#jJ6Hx$LDBd;B5F0%c0>;4&Ln(U=m&{<g_S=_sofG46CsOkm0meo|Oeoke{h+~gk&#KP zXOD~%fduref_wi>^BVye>&;WV^E2(4pL3qck1KoDz9&nr2~Jo3g}09u#c_IlDk0mf z5bLVh%e@|3X&Q&*Wsap7XP*}X+!oSd+}GBrjwQ%jW)| zN;_Ff*W3Jxu*9Q(c>xL`(PqlRez}d%$A>p^nKOs zmxL`!S!2jCI@6TLcyWuK@QIl%5_%tjf4-sr%4p^^6GPss74_8CUJJs)H;~b&M3R8Q ze!Na7+k<;Q3Wr#oe@tWq7%h)Fy?zB>5250=z=aK1a)$dfe;aR8c-oW*pFJVO}(n7fv3+oqn;}GlbrW{=Q@o)!1=@ zXY{;8W~5V8iT2Y7V2UQwzx>DpVI5q*GUj$Bhq#AP*yynX)4!0Sg)~n>13|lXjj2pdI(`H+hIVzR`_E>_9?XQ1K z5iy$V++ou%ggA9M8F&=0Vm=!ys>PZNvh}UXZB4gcJX-ysi;+C2tu$yB+RtMRxuPd0 zH_!!U^G*fZ=qtsFM<2l5WOWp!9>k@mXgR~VYKag0zo4ob* z%Q$tNamjH=^X8cg(q%sP_9~ri>;{C)D)EHk!5RbZ9R=Fa`-D=0T@}6}#r+`4D)Gi7 z1ZrK%8~fekpIo>q#;;chFmg6uR%G|pSv0ZXCMk4prVqZ9SCp1@eql3RRTj4m^yYY7z3({6RY zPSfZ-0St`5Vp7?hsYr3`vA81jilV9FI7sY&c6NpueyiF+?c;9}I zl>DgZLFQwC)Uf3$q5?)_$@_~zJ3_k31k9f86rtE;#$x4w)~$xR=M@)0Vc3!&UR96H+?#N$QSeNR4^`pWDzKGwf4(iP;E) zNeSD|$HeGr_5Cpm66R5g_xjX0Sd$B$l9bdGv+iS>X`xL4F2({|GZF-6eR1<2_A%KXo)>Yhl`@ae-5_&|Qrha$Z}ZiVd;2>d{#G5)t^G zb-ZiV#?>Q`9M{yO)~Hh|GRNKdqPJm9gICXOiE9(7j^?@hG2^-U?9U!>wxIKS=;qSG z0NW@ACDrU^FiPz78_+K{3@WGpMc!M7MfI+I-xz>OiL{hbN=r)%3@xCf5LzqzzjKXrC?MnOhaDw9F9 zVX`rk`u^zQ+n2qj`Yo&x#29e+e{kL70WzZVXp3@CI-Ir=ktx8 zca}8{*9Ow?Oo)DWY4`^c{Rhs?UG>BIgn}n0Q0f79XK1rwXge@;bONCr_?z?Pfq{W5 z^U`Ipv|^b=PJ`{ww_)se{=p8?4R8wgcj+a0J;rNjxrs%oZq50qd=lK&U%!bk?+nG6 z!Te4c!@9UghFW)BanpWK)uoU$Nb2>eb}L9hz~0#oGNz9nC;>`PzR^f;O$oY|3)RAU zNnlos4g2nof4@-(z9K@2thAG&=dD@pQ%OHk{i%Sh+k|y~B0@c}B4n#|n}=hN=q=_YLggmHzLa(>~ImCC~rb z^yc5W^&duJ!Z-go+2~Oe+x=jw%)BV$x_#h8K?h50fol*kF)NkZdn9GlGXdqHYUa9*ZdLh2#fpx{d_#hbHU{Qc1x#!G&&zC-(GKYxFisLH2U++e5NAr(t~(d)+VJ(qO?HK>R|5`=+p!T;B7 zbrK=LVC;Z_M@IH#7PlPMt!dSrlBf4^8(!TdCqk-2F(P6@nkZU{IIrP*hEV zICxQ-H_g;{;_)I}kKWA=+MbU8WTs7XuipGd$gMab@vZRQw(+ezuJ3ZkTMD08U>|$9 zzMms+DcnhfHm+<|6N|a0X!WfS|H?wnZ)l9O)K11n_ z!mMq$lptW6usANqu+4uv-r3>xb@OEE#mQ@aUNHuPjY*c{EyRobE^pmZJ#*fZ) z24gwi5QnA7!8^Q`;Ya!uuphC6@BN=k!n z*tw#y5g+$EGID;(N5&U~A;=7;a%(e+p6y@fZ^kDBwzh~|E>jIZ$6>PsN9YH%8&Ls> z6jS9PcEgFs%%IUE(fX(o4;CJ%{4KR0+epU!X>nXewUiWh806>_hmt%G8Cgu^mMQyp zF@96_;dUW{aW)97{<#$`3ZCBS2;?^1x2WT?9VnpwZK9fxuZ zxsph+VtDiN;S2I7Rvv-is`Bq|;?915c1SJSa<|xRe+42O7W^(|i_q793j*xs>`t37E@vHGVe*kA6WCO-o(p*Gz^ zl%Y5T5il2S3k>JOSRE~AaaIP*;MzpLF1@d7#MU408)EKhyOqJr&ed!b#pWrnC zzwv~aG@{X)Hw)D~=ADhz>y5mk7x9bnduQ_sJ~T{C@t0delk1%-2n?Ac`^NFq*v|g7oph*N(8`PhW?2Uh^FLke~ZGN)1#0bXQQx zS7SM-V{suNb!H4e3v$-&5sc|2SKw`^IaCihABg@OmX}rJ|Fvt&S*6E{ENZ3b zT}|C(+kKOparI}5hhH^{xA2^HV3=?Sb*;nAkzX7h8?W2h)5+kK=~d6J>6@IDw>iWE zX<>sStS!+ZyQ@k@H=8@B&)=SU%FWOgQ1o6`?7Y@l?~73? zHwo?<%bjc;=z|hS3;R~|Rey{#a|zv0ab{(p)3Z{cSjf&@^KJXq)}E7CR5>5m@6vC} z!@7ti-izhz1UcnFGo?Er=M#anzRk!So~(SvZBH(6fZffQ{ugi z&Vcdblqb(3L)KE5&fmHXNuNA_>llAECLJwf+2Tf4)oZbMttoiEdbxWguCP7u z;5iQ)K35=n6}sz1A0vg#Y!#lnV%Pd*`fSsA=kyXTFNtZ`Y4J!+=1TT1fzR=e9T52!+Vv8o5E+lZbJaEol zOONsQ|Kif)da%+oT*{PotvR^~#?mN%+anP-s-$v3;($dux@@fE^fhi$;aH|j^(#hI zi`jXy(z@$!Ls2EO4a#ukOG=S2qx1K#RbCh@avV~BHj2?cd0$-6<8ow7aJu1Q z8-9B=B#qv020rVJ9K#hj>=yNN#(F4;>Bw2YcXp}Lvez!2kouAzJ0jf%$3?U0A($9a?^jd~E}_d$9M9qk)dg-eR&0(tI?&Fk!I^jl?XrbgLJvACKWP-< zog^NI{y?2y0#`TtTS!o>9_GP@)?v8Wz(PRSBX0ZSlicf1Jak(rDl<~VW7Sqht%7t7 zTFv+bu+sJK(x=6?H%GJl@cjU1*^_la!7Bc`1KIY#nPL4edL1#>IImYx`70}kdRpv3 z1V>T5n+3u)o(Fe91S|0n@ruygZ6yD@o*e!ya2EO?T}NhyV>i@yr*&+hRbe+=t@-l!$W5*i;YN{I-0EoTqC3ID$F ziU-#05QC0Bq}L&haJ=*yh@L4yjF@!}=QMFCOl+KR>%y71szuiC~W<`N`q7iMSN`0dtB zVPWr;@}=rc-(Zqc{Pc)og&z0Cf5xx>L~cwrOLjwO92x1_@CI>-s!dl?_EqSEqR(?} z#mBUyWMRw1QRiaXdL zar~y8?K|a+UjFOW0CrOBc#RVW(lnk1{;6qxz5u0Sy5p6UwqC{XlqWjJ+gPg6JT7-^ zDdHEQB6(wwGd2_?tk2H7elDwv1h_8Tu^S(M=}w|Vt?b3~6ED}EXHi7yW;5Fdpc4t} zr5>1$;H-{@vU8hvEY}@JEp7U@XYf2Z!5U6}zSiR;;{RHnoSCkIM@rwI%tnR@o1c~S zgeKy9-n@s}Vr|e%I_3(VLaQg4Wo2Hz!=&>zy_YNPjyP|BZES@+U^Pj+phJAUOIvJo zC)n7>bp@*Fd$#0w8gJW_yA9@lw6sb8`7ybL+w36^;1UYq7qW>)wN> z<$VuSe}=CvCOLe`xwkqy%_g6GL#gGV>+&INf&=3?>+9RZh!b^&yhy@wqC92WnA%*P zM1@y}M2U+JuDo2oI@8Tu$|q$JF6K|?)=CrR$Q5CgFc%&X3+oMjy~xd+z3bBRB7&w; z`Nlx(_+oV zPuKX)?uf3(RiB#!u2KbAir6@x^HNpFE^#{@<(gkmEjIC@L@oh%pH1>f6Fa*7NibnNQ0Ubpp$pa<0q+OH~xTy5F%wQxf1 zGG?7zbibhOeO&*BNB)Znt{BE}Gx3SD8Os25l30M(`)wjN+s>uv?D#nBZQ}k0Q*xy# zb2!pRR7hK{@lTp_=Cei#NLX;NOo&NK88wAFI$OXWv^$Q6JFItb;o)Rf3hv(=f1zRDm5sbr|$GPETp3bb?@{V8-h@ z;YeEKXU2^RQ3*Ay(0d2SQ)a|SWFLC&X{0Dt!T8cQek_al)3K!$5i|p8)rr3Kqu1>74<=9xr2&fvg?cV@?DgL)?!ik>eq{n z6hAT(?WD;{OH`ELE<>eIQ)0e%z^h0gUYeVmn=ViLyR%IK9?N7;PeFYm${eB0<9QpB zF{_D310q=b-o#--LF>mCtp)c>DGXekj`8qgqBUiv5+2sGqr{_~qOz=5(X*xfu4X6Z zeRA_2XOzo?O)E#;Es0WdV}Ztel9gm#AShHoV<4U5JVwz#3>K4F>O2ZbsA{sG7&q!l zbT_Hsg(Z{pT<~4R$Gc8|y))9+NiT0>p|2i_WhN-)$U)Tv zAl{1{zS9Ot1z@d~e*I-*AUNqrna1J^ZL!)K#GU@ZYRz|u0}c_ecKG|-=LGr=Pv*$jpye?cmKKoVrJonm2$)jze*^+;Nw{^aBVF@x1W2%^s zRhsxlr*<37r+d4pVYvH{n?ql@_vxaxLZ4`ElmN`8&XsYZcWyiHXjGe`l;;ig9_iAW zHEFKCr$blAt5YeuD~QYd<#Wmtp=uf~?!n%#PR`dw&AJ5@Qh5a>m$;4fA*Q#h%|4ha z=e;b0FcCAwVqmFNLEEfH63$qz7n!UyWR#q{o3D9-I1X`Q^|V$^at8@6nR@Gbtx00x zp$pTly{*QAL4mc6)_8s~b%}k}Bq|EgUq9#KapvbpL0O;d2u~*!P1t>1tTK_g%F9s; zT@0Q&Z|zz2N!yb{9^w25eOC~WT(@2~tuW{K-szi)Lb=$EtMImDeL`-zS=RJ1V`3AV z^@LnZ=Xusy8ro$UkGRgNGV`|z2e+@qJ7F=>W>BY;q?pUqmxTw~eDFuANdmBmEa`6M zZ_Do9vuN;=Ran{_R8mJ>hqSiBLh>OQ0lpRkL!Hqn?MIh{Y=sAHm42sBC{n+1tztZV zpDv&Uf#~IuMlt=0S9eJAUrVWuxFU9i?PKe!?8-*R#D0|x{S3N^N2Hiy;m4|zWk~C_ zKcsjT=6(xJ_$=wPTHZBELPa^U{3+yVY)E*YbghQjv6jngBWp}T6$-lQLmz9^bFftQOnh!j|34#%eY-r-Vg|# zDAy|UTbH5k8*=sjs53OpES9FY@|Rdx);)Ei*V57g)5Dc67Z=yOLP=_>Ff(&e=GLum zi<=Lju4gnX>hVP&csqqUti%p$u!u*lENK-rIEu6lxtQsdfLjj4gY8nh9Y)d%%QzK0 ziL(%iD-`A4mlmu1MTUYr$K|2^uO}3s?r&FVVNa45oz}DRR@G&1O%~3+Vtbi2I0&o* zxaN7%Dm1xdZ5XEegyDK!% z#BN@7eIbp#@^6=XJZH_INYKG~wd&axVGspJgR@v8f`e&U*(HLt%fZh&+(m{(+t(u5 z!WK3;n1pNtH!n(KS(Pgo6pNc$;U*w>!Y&9;oEl%rXI8j*A2 z${U7uZ;jaO^xaOH7h3a@!q-{-Q|64sMD?S=xT9-hXSbNzT3+mZf~AFo7gfpZ>p@r# zs)LnsNXs9ycR~`po5mF;w!Hi>3$wHPdwO~fyfi*uej=pQFpBByHd;=YYUkfTWn&-p zYWjy4z&t>rN&tOMU?{y&pK#@5doskmJXVJgwS2>KR6V|(Zn72|sUIZ)x5{><8v~ti zY;Wm~$Khkt@&`zB0>kNuMbW4p(^{1fsqqui|B^-iJ>SH_uh)WWzKzf-B7pawHmbN_ z^|={uh?7@1(j$EnF_n5_R6gP6aw?G_4~BoeR?&!wp=fuERK`MC_+!HP2l|#qDQ@b=aBFKNzql_|A2p4vxtdI7sMv>VO*h?ycRXEE3SJ9FqpOFM1P90v+2xf- z5s8VDSM5IHr2>{s*y=ys=WgPv(1o_ZDJdX!G4E>HVkb}a;kXi?`$3xnqp zAPpwZ2#4j>@P?N$DqAWwPp@fo@Ag+1szpZ-xa0CLLU*(XgAYadd@AmHbB2XURhzJ6 z7*fbwE!lHluG}wQ-nOtfC8;cRREQI7L*%fVP>QWDmUYYSACwKB)h#dgh0dl3GnHP5 z=$ET^ajG*CxMN?+wW1Zl_D;oK*6bygpp)l);$(SVrdII6<%xZf+mmRM@`(#hYrfiY z*MeNVN&Gc@6a7hA;hWF}rJ#kGFQRT{DFW4Rc+`J|y^)<7EHGW(s-Oy8?7ccHP#^0g zmCq#H5sgMQgmeL6ej9STZ`lZxn=?Mh8#T>BBLBc_?^?sVj`EdKAck*NAG5j9@A z^N@e2BDhX}-m%N(^nCaEOb@IUwShW{E2+}l*SwNoqkGcAVqAOD78m~kJ=*H~+Sc|Y zx$Nn{X_K2Q#;vvuj(LWgBsFZcF7gS7N@Z`ne0S`aB6zOMDkBzHWXVdcV7(Ns-qc<+ zC_iq>6l=qxW<81;WJr2%?#1d#G34o~vyP3^NWLPB)~r;yo@9?<&SAVFQYq{xXRw#W z*RS-seq)|wD?QD3*-ikD_7|%^k>ctTD7YHBeah||^%~l~B{_5*SKY#~wo}`CFgVl^ z0ZfvdtajT?3pRNLKfO`Z@E6MRPqmW$R>WkFpZN0Cn+E9|AWqp83@5!xVM6x@%RBt_ zZI7@))W2U+EzNt0;-^V_QzXOoCH)o<@Rl1mcrDmV>c<{>KUTiKepchEJc@WIAWvRYv&qHs975VL+TuQUqSy;x&Zyn3R+QA)YIVBW)zZ|E z1?j7-euPT0Z>|UY!&+LQhc3Vfhas=>uF(*e2#LM(hhyPYYmH1S&CNiGckSt~c0F6; zQ?V>K!)85KtT~aBFO|oy=C3ulHg<0>Jj};pFODvrB=)`kv%b|#vbUu3A1@Oelk^%K zW`4G!p$LeT`)Q4V9WP<)ifZV1pYnKJaN(F)(L^~z;BuR@mJJqTc#7jBFtZ-FOyWFY zS)4EBwzHhQEktUm?&rzF@=m zLnFDozN(?D#z(_Qth3xDyGii=*!U{9-r_09EkOVHDE3_*4KPt}EmsSl-G1e#KWxw% zeCa;m=*ZmnUGw1Ed{K+NqvL8(%koPmcC(VWvH8^3JdJKY{+gdV``1%rG2|#duD6TV zDq^Ou-B=z`>F&5Y!@nm391@pzb~3<{_!kCR3{M#kSZq5u?A385Xl;8D4EK)^!l9{f z6hR{C`)iiIG>Sxs?ptu*I>s|d5Z)h3!h8EWc@;_9Q}Yzfs+t-#efpa#5#AEgTV*IK zRw-#M$sR`H6_g)GU;UN5PUU&KXron171s#8d?h}#)rhyVX5r%Uy+Zf7#KY}(b5>^V z?r2|4K2y*o!xY}H5PNzAqVEbF^|Wz!isZT~opkB*PUO`Pyt)HOKy>^-ilhK^c;-=A zbtQ(Wg^z%^Qs$=<`?)#KPpFu`m;^t}HF|z>h~WwfU{A*Hfu=vfF}7iTO|mV7E@=Ro zgq}52BAI6qd74u6%tX&fa-L@52F7WY55>k{(P&;w?54fL z%ah-I{J8~77e>2vijt?M=)wk{>1;iPqPtQK@rB-)=ohm>?RVBDq&wER0={IhMI!#h zlFX!m&GoqV3z#+z6=?yXeVM+){<0q&ncSC`?h9wuH*LEOmpgM!cM9axL+zl@LItGT z-zZIIuB}31gDfMVSQMg)H`Rjrbu~f$41sa+^p6WDe@CW*Z#>MK*PO_)->yPeydUwa z)si>x4U3&y+_beX5pL*d%6B|~_O??U@29?}093da?gNi|NfAgQJv^on7+CsT6q&Sh z!hTbQA#8tb$j^U)(=sqf&gfb|W*>Y#eBa03kx`8?q~C(yQLdup$br2RkH>rX}dUAj5eQw-WmTInf`rt zOYceb_^Om)L^v(zd;LCfxfIMBc+qTXOq8kOVm~u2u(tjZ@%nWiCkVG;`v0;vb~>AQ zmWp@4MlyCD%gxKgxNEs<^{+^LoNszHKt2OBLR|cbj7~J2ANA3p#c=0%bBr{y0x}Ff zvwcAv@Kh=XUy&2^ewrDl!nxJmnA zH7ycE?*$>DZ?T5g%)RaQ3Xuw5z}~LJg8>O?n>I1wTe9 z2kv?try{WrpS}rK%Hgte&_gx?5>2C~ua~!C->)owQU~~5>7KkZBqS?xwg?*94#Q=K z0Jfg9ArZhcidXYLQ-;!ydAoqZEdRR-z?$^eEA)S9J)7Fj^*6Ptps~#C492aGn5bj( zJi*F&)0K{4igfn2wnLl8Y6T9;t!6oj6EoXD#koDpZW`HQ3r)Bf@WaRl-6TQyOi<7M zv!_SqU1SwUlg)i3zPT+q+Bk{Q4rpOGqGxNij)D*0Mx{c@06PLo@)Qwo2!ybakdr~#GJ6P*Gn`4Ol z36QC>Q=jH{!`rzUB_F@YWuH7Kl7`2#`L8+RczL%usP>NF*=%~Zid%dq17UbUw@MOkO~u)VX`$?) z_0u5XchNkIDRy!RNe%!OH^AS|5yvA$J1*9-^_B1WQb{fs0XW7yQ74Ksi@bnGoSgtM z0$s8WH6}(a*vEqwHsg}+VKu1S@l6Q!8CtYU-7JBR^mrt_aU?qW!*TSl*#?^Tbzbf7 zCmaEmV|A%+HI}xhBWaG!xB7!#pcGjN{^LL-Tw3R^Xh?HJC`C(41I*e&fcl7S46RC8 zCv{rx%bWFW5-A>^3cSQ!^@I4gp%ZF*u-y6jvqfry4`r4G1Jw&<9g-wo>~022*D9scog)jPJ}kLS`jrk;Z@XT;p|`M zP?>*Xh+?SOrYHo02=F9(jjHnbn0Cnf+2BHC-&0Z@Kk3^l{VhWbU&9(3J)Rci}_e{i9 z0{{{pq)ctr&Gu-(aH|75Ym8trx9*&CB!P02>HujtuhwbMefQG>r4 z()U3j*hE*j-i7cXs%h%HM5TwD>>EheP>lc7)D$oY1(zWssgv3PgP|3C{Tt#bVV79{ zV(!0S3|p=Hy5~--FY3{dw1adA0k;1{^V)33_v8+j;O^959A05zVSxnz6W2erUzo}G zn+nZMB+Jje#54wf5Sy20+C_u(7YXGzoiKKoQ=6GdM68pevGc%8`;eX43IqB6%|Uoo z+6sBdL@>Pe`e~kg@XxRc0^Oi^7R{;jD&j8P3xF zpi_SN6Dy)fzRO;ld2vxi0pjI;e4q$r{rge>P0En_EoGExi(HZ?oQDxsb3DaFxUkIj z@yOLmp339>PwU%%T=f3C7sr}_9^nXhLVZI$@oixFQB*wBeEZg=@^-hCiUOTz${XnojAM+Jnyl5df(_u|137T-8_FnXM+_zD0g}do%Qxjb>Qq(iQZTSu zjj4XMjlcNKl$T59uRJLIm;@A4uI)M^yEiK~dEz2?VNP$Rd{D+%tV7r{2k_Lsso@AZ z4V|MXpB;}u;pNsXMX;NR#z#i+CNg#X#9$vFp)Yz_wltzxaL>;o=nh9+pP0);#<1O? zhw=JxLCa}}_^NvlhIOXAN za*Udi(htF2ey2!}H%dz7f{f+IA$NNRV5gk&&}YVFiAp4D2Nv#8nEU3P;x1yL<7_tF zm9*p|mAyA*!_Cb>SBv2De3lp)>ijXs8xnZ@x-5bOG=#*=#en1>b8}F9X8}gtL*r2` z%rx0MS{Sud6y7C

9)^+FRl4b=Up;Mr6MzWBo6yQca=5Glb+owUAsRq4!+ko&A$( z6L?*s3axgq_pJFJB?Fi>a&wQK`)Ir@>z}~8zy3!X85^{_qPe>BC#w1Df_8QB_5nD8 z#1d6Xc6FnV(D1&TBKzZd0g$=I@rT!p34}|XSK7?IyV`xbCULdvzw3Ur8_MABc4evY zP_F?P5^qcgWWAqJLc7s1(S; za_;R4K@e=htrx?6qOO$==|IeC+jWeI+acs#f=cL>>i-t#oHY*)axA%ODyZ ztIi+sU6<;~Axq#go2{0dLp%1IHgu3ae-HU=Do}p7~d|xlKW@GbSrv53=%1-WM;(;GDvRgSx}kl zJ#1|2^hub2xRXTml8{*>+~gvB7Hc-b`C$Y_atMX4c~J#l_(f$}zEm^Kxks}vbMAJw zlA*2ZtO|9!$7@&E%Zs130Sf&7Mq-b6Kl%SMA26hA_OTfK)cy?wF2-`p{0&h`@mLY( zVv^3gt)jq1A${hMDY{GO{N{&qIG*O)fxT?l!-0YWn-0p8EbHS7s85@kIiBi~TvygB z*PaHcl)q>*z7JXClLb_O31Pics)W-UNOsr!b2GWq@n%G(_9#fuJ9d~hmv)!XQ+tl7TCq8^1;YrNfGOP`w(wc z#Nk4A-C6fnZbT37#ET1Y{V~Z3y&E56o4kEdw-=G-1g_%m+e>HH#uq`S=T;YOV`F6N ziO4Re&1W5mM>R0-#(o}cSY7~p>$r7*3<7C^m;J1C5Ojq<1)~Eov{~)UXQCGp?XlQh zGQIu5Qm@AO4WR!L^(L5LDOIo>!B#h8twq*y?;ueZ>h5>9JBE|8^kkIcM;P&-)cE}! z@`U>$69)Xq6js0VbsjM%e;*mom+HwKZmUd0O^38fxH-Fp_c)68bkr-WU;h<^iI=DA zY%JJNER<0z@8+L*l+`Mk+~DHVwg+4(FXxqTy|5f@kT(CgyydC5(JR<*QhxTPO7q2U z!wFm%;j=28wZNIle6e)?Aqyy7{{FeWE4ahco!LDSmy944=_A#oH(l_QIPhcK%8`C5 zzImf_^B4W8&~kF76QZ0!X3aBU@}M_2iE(auSxHf`UC_bS7KcaY?c0=Cl{+S07x0TbZw~W+jrW7;ns0N`*67y~5DoTXXHo8=JObLn zY{Zq;85^7WFJ&X-Kcq^$DrWcX@33dSc1m5P&TZ4;hlHE^^vjMC7xqAM?6B~@%|}Et zpW2z*{gPX@5524VvT#G523gSP<-O>ry{J3~NX|{ss_Az1i(5UvlL@xu3O2t<+!URt zq0_ABXiaJg5aIwOJRm!N%jDvw48(RlQ$#N#Ka9=Wq+gn;W?Nv^N(`KJV!XhmYSsE$ zYn^#rJzUXZfs?tqwfHxd#ah_1{r!()WuG_lC5WFcZrJO!c6WAUAy>%N;1l;_^4!Xu z<_-Xqm&8F>lWg=dplONhH7&zy_7+8)pWQGGpG(Aj#hd#4bMyG3q$Cd<*gz>S4QGc( z6RH@6v!0$=?vJUl>)hXpQ3&~xaBT}0Xw?Mq`ja{So2XL^ADOwTvn615rMFQ)`XJ(q zc}UJ8f42W87~5NCDns+t0SPvG!i#~YE~VsEU`t*%v8o{H(%mwoekl$P4pi1b#=pd9 z+rQ{OXcPS=XrT|LoU?GlEBcwtc*)wVCDe_WK@f-O>ge+?_r>5=<8oYbFDbWEHRA-Z zZa_`7<{lS)or0@6a9il0lZpW+;b)pDL7b0{dySs>f|JfYXd0^xi^)z*tvHo0d_$Wc z32uJ%&!Y99X0Dn@rN4fSZK}I(#;Th;8+Fe&^LM`sPs8}iWooki6`0wS48+hlHWyG5 zgBDe1(yBcV^ef7Hy=|x}kV)reILt5XJbYm0D+kgBJ%L6;X_Qv;WSQJ=AH5J;5THBj z9hgdl@JAoIkoD9{>M^trT-$zqGY-VM|`P1h%}(L4uP$>kQ$y{^D&-wCfCKx2xQ zGhCGVpbJ7Y2MEs1#jwL_k55oORdWI z&pk0sCyv%3M`bp-&9l%iWf}N)nOgWcZ92~?j2^rivQ9@BBhMVW1$iz}q;KMEtXB@2 z8f?YgALb^uL{^umg2W`)WRsUqqqe%*bsVMeu~%IZT6Gb2_Zbp*?@4JQ zdezJ~kOcqdtg0bP_3+n;0`+pq0uMYq211Qs3oYWc0$5sonV`pYHxlQsfwB17<0_##pGvC#v(us)%WWBJGwz^ z*end-4#IB(RH&2F>sUd}%y)a9ET$suzi#al1b~@rY_OYN#X#&ZFXVB>k`*#`1TYqR zz^E2o{~#3*@Ua(DBO#FriFi(0Rd8N0h}S5n;{LgzVpSBFMwza_1nPYC_P6OEc+vSv z2kr8vt5+wl6yY#xeQzrw)Z9(DJ59#{(lh}OL%gYT!YYKCJH>*#%_;YD0NhE3cXIgf zE|QVsNsz>YuU|QPc7~gVhB7(obYz;uB}Z_;1Rrqwc2>S%2NQYf6OgKnIgaLkrF}~A z>>!bI=%xagDFuJAo$AJfX#+OtG;s55eEAb@E8)+Az-2@4Ynq0&&(2EX4_@EgZmds< zp!Koe96h8|RnAy}FOnCjS2oOJa6CH=y^paa)EZ88wLDJL#sMrX&zW3Hpm)f>-6tG+ zStQ`-adbqc?x18?9t!R?nyvdt-_o)jy-n?f;->q0HfmpXLlSg(WmGf6^D)Z(a+m~2 zip?JlB+>ui>L?oMUjEWRS1Y@ub5mS*R5oUG|E<&Fv%5(BZ%2r${4mMPjt*ITGz_?` zJi4c-=xOtikZ{{2Awr7QTYa_fg1U^yso=v!_ECl?ay=e8wrX=f;~(J|vkviPjn_U7 zekwVaZq1e)M($yJ{*VG#tEsI=Q?qH3LB}Y+rL$Te>-4Vcg*+mh4OgcoOV`!_SQc)? zY~YpVG?%P=yFTU8VdtfkztLIp=+#x9!=|Z&jV+0Sg>c>3z42RZU_Va7BGwYm8`+tx6{m}?sjgl>b}mrnrGH^{` zgwUna7sGg-*Np(x-JEuaVCA0CVez8w8VLzZyss2JgeyAJx0Y{T&Am)=I^3a}U z&hV5EUDRGT^Yhae9~?Fp$3ddUsD-u`_d0#Nc97TDrAj~LVb?*QvSzX8sh*fQ3iQBf zd1kjDW{>n8;rRny`G%s$fIo2;>`jRZt&;kbKmAnI`@dKPqOcmg4|D8_ez-~AXF5MneQ2bNc5_1Pz z0SX%P#yK+MA5xV9W3ahQy5tpQTkOg;YqV=+i4@myHv%KUouc*hN=?&G3ZN zce@1g_x7k4z+``R&=;d=4J`EMr+UO>&K(u0Y~nT7o3@|*6!WF7E1yZDZ7^}WQBb*P zUda7dDuqX~Dy&(`{AjvP>Xt4QM6sSNYjgG&pHkowIcgMP@~g7qqkJlx&^+OPLa@ zQ9twY*rp2$48Uhw5g4`^sE2!A)J#(mRefxg4$NV%zHD=P|lGt`5i9AdsKYGSCbj8 zfp-w3w@>AE=bR_Usaf^zMMR0iJ|aIA+0uW1XL?fEX?=Z30wjyXL^UVc z*43qUKM$IKUA(lw;;Y+I5L^n9rsa~6Xvj}VLlLYu8FAgXo%03WCG9Af@P^u_7-x0A zBby1h*hB5IO4{?VKwjFcYjm0ZP|<#ReaX+nJMuvd!8u31 zu<+03m7W_9lXWXdtI=dhb9kACXf;KRD+a>C|Cdl3w54iE3 zM~n5LIo~or$a=8%?JZ>4^1~W+R~{~~>ghidIWNpkifwlk)k#te%NKpHne+k?^|hRK zR2F`?e8fWKv~sN7kc=97?S6w04SE}zzLLkha03p!y$>B)&l2Y3G0dian~qI`_e zTac|(-|&{qSluRq^A^XhR?OYL^YJn|fIT26aCOwpc z#x7QYq^=56u7{nV*aCqjvK)sfDAC$?WHuWIeGVWeNFfNnI18o;!2=)o zN>3t)KlRI94_9O!_;>_4b{R{%H!ru`?Q41oTte9YK4XUy7h3>%c! zGM{L6lS{nBcJEWh_&7>WM;G`7egMBY6%1X}@^I_f*KONSAIb$ag~Z6smKfg{*(dHK z7WybWkmbNGH3W_2hFlolCfUf>v4A=ke^8|)Q0iB-{^IyJtg}#^fTxsUNUye}(F9&` zoWI2y3yr$?FycYsnnIUb;I+I(JauSP7|Kpg&Z<&!Tb$^n)m&h7o-#w2YAxp|be^1D z$1Ck5pPt@&xDaXLv+jx&W0$1rAHwCTsdW>DF)3^)9G^)UJxV`T=`rc73~7z!W>LZ= zV9#3SDCf92qkAX2bb2*J{R~SVPB>8!olo}Kb%kyz(0C}T(%lllq1D#UKjEmPKDa-H zMxwCaG{R-jBI)5XC4tb5VtM0Z*{Be-rcBt~cai6}uCaW52~1387z0(-x29>rLFo-? z#SVHK0$ik5w!U**etNv+k@8QwY*qWG*PZ9n*UxA{NrRR=vAW5ZAou+9HOJdf;o=&QDVZmcj2op z2NZq5bcclLRiX);d+PF$Anag8NVx=2XY|Mv~}cYgujJ5QGnJJQ?J;2EA) z<5(I1#Q&I9K{jB@(qDTJPq`fC5BlpH235?|&Jv_Lz#}s`xo96Ry zB%}q9%@s(oME)>8nN?bskFBq|oT{O1i6JShQeTQF+AsonyoAJR-uj-jzrV}@sD?}f zFim6NO}#|d2#FGN$-i8qNJz!w_0okAyvbGL?zBIzTB(KYMJXKxbmrYPr!mf{h26v` z9o-MVfmN_hJH`5T;KCd?mn3ORI$u?zO5vU@nGv4=|h9qo@e*!*0x#)xZ(rXN#&M>{As`TlR_LO6a zTSyZsrFUYJwMmITlj6>=LfkzYh%?dEZPEuzPqCQkmP%M!B0b`aJX*9m-;H;?akn{< z@S$?u#*swm9ltAL`142$t1i=Ed=Oq{>s-`h#opZpgqh7{b3$JOBLl?s;+czUFxLa$F^F5~h1ETPqUzF8YX~JAWLu+6 zCQyUXcY_pbN}t1iuCa_7VGyC-ZS#e>oFLy6>kh?iTxJphlXpPaSWk7I-S$-+cnxu; z%HHAjW)t#gw_~4nlaqXmA8=QJR7Y@#%9cskUl)omIBW& z{(nuZ|Mx8y2J_3|5nA!b8HFJU6eZuYsNm?kGN(6^_n+NHBGmwfz*D;ys@T2DT7o$) z_18#Pn6H1^mHrJn8y_~ho#ixe-Y&6n!Yy7<~~Bf2(Kp+8d{O@&V& zFHC{p{{(geiTo!Mzj?cy2oaT^tN5Fw^Wl)LGa34%hh+U<-2)zR>OzLkv#zesGOy!o z>So#6m0Zs?LtGeo?9}V7|5$(Zzt238Ft&3SC+7C`n~UNquC&*g06-gS#}TgcBX_DM zt+?>A#`t@Mbh61kYMk4z-&`HB?8-+~<~4pjU-`#X7ss!-nyK#`HtNx=4=$%x(SJ*C z1jbbioagc4!@s_;>Z0;Q^`H^%)0p-<48t8XKMBSf|ZjF4TmkV|2MEaX`n%e(i zXXep|cfB=)h=_jcCFW_^=-R{ik!oWZQU9*YLlWuHx1>b$+po^vWa3w_ZEfw}6ms@( zdCaNP+9-w5v(G`IWz?fP}V+(&&o8`CTKXPmwJc4N5e zaf7-%a_i35oJnr%G%BzgQ&K&?hbxiMiF@esv;``SrAR1`#q+3EjpU<}t)fm6|>e}Xu)QO15=+x8mRJ==PQ}yRr z75UE9Ti9;@_V1fF+^DJKh{(kJeEM4Nkl{~!Iq*)I!MwL4#lJl}rgz@oJC94&=KQtS z`+pyDr-ry6{~H)r(bgwoYp`Nyo`UF3%{Lb-qIds7M0=0u#{F5671!fV4OLvJjM#2o zGcVn`wZ|^#7fyfl%MC88R$t#fyzk$h??3qu9MNyP{{HaJwR6R+n0A;1003x5 z7~ui{hy;NP03ed)1#Z|=9p!7attXtzy?#>ndiwz$9L>8*0RRA63{wgK03t!)0sx2v zfeQd25(F*)fJhLy001IE-~s@M1c3_xAQA*F0DwpkxBvhmLEr)ah~)nPbUA==8GeAN P00000NkvXXu0mjfah_si literal 113178 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 Date: Wed, 3 Aug 2022 09:42:40 +0200 Subject: [PATCH 472/898] Fix Typo --- docs/source/kubernetes/microsoft/step-zero-azure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index 0269a0ea18..cd7871b7b9 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -251,7 +251,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --resource-group \ --enable-cluster-autoscaler \ --min-count \ - --max-count 3 + --max-count ``` or update the parameters with `--update-cluster-autoscaler`. From f383a95bdfe13d6f52bfc8fa487e6526d4f3b558 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 2 Aug 2022 15:29:56 +0200 Subject: [PATCH 473/898] use real jupyterhub version instead of coercing to inconsistent semver format --- jupyterhub/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 1c0507f97c..d0271e7931 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "3.0.0-beta.1" +appVersion: "3.0.0b1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From c9a1f77495106f6a4a7f4af285f95d8bfeae6daa Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 3 Aug 2022 09:48:01 +0200 Subject: [PATCH 474/898] Correct requirements.in comment Co-authored-by: Erik Sundell --- images/hub/requirements.in | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 188d128d9d..99e982df97 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -1,7 +1,8 @@ # This file is the input to requirements.txt, -# which is automatically maintained by Dependabot, -# as configured in .github/dependabot.yaml. -# It can also be updated by a command described in the +# which is a frozen version of this. To update +# requirements.txt, use the "Run workflow" button at +# https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml +# that will also update the jupyterhub version if needed. # README.md file. # JupyterHub itself From dbd2f2782ca43712abae8b3da8342d980f0fb560 Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 10:17:23 +0200 Subject: [PATCH 475/898] Update docs to get public IP of proxy, based on kubectl recommendation After installing a helm chart, kubectl outputs the command to get the public IP of the proxy. This should be used as a default in the docs: You can find the public (load-balancer) IP of JupyterHub by running: > kubectl -n ... get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' > > It might take a few minutes for it to appear\! --- docs/source/jupyterhub/installation.md | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 31f2b90d65..191cb136c3 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -133,21 +133,7 @@ can try with `nano config.yaml`. available like in the example output. ``` - kubectl get service --namespace - ``` - - ``` - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - hub ClusterIP 10.51.243.14 8081/TCP 1m - proxy-api ClusterIP 10.51.247.198 8001/TCP 1m - proxy-public LoadBalancer 10.51.248.230 104.196.41.97 80:31916/TCP 1m - ``` - - If the IP for `proxy-public` is too long to fit into the window, you - can find the longer version by calling: - - ``` - kubectl describe service proxy-public --namespace + kubectl -n get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' ``` 6. To use JupyterHub, enter the external IP for the `proxy-public` service in From afca5a2d2cdf94b2d811875058ada8c985add39f Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 10:23:36 +0200 Subject: [PATCH 476/898] Fix phrase --- docs/source/jupyterhub/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 191cb136c3..6a4ebe0f5f 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -129,8 +129,8 @@ can try with `nano config.yaml`. ``` 5. Find the IP we can use to access the JupyterHub. Run the following command - until the `EXTERNAL-IP` of the `proxy-public` [service](https://kubernetes.io/docs/concepts/services-networking/service/) is - available like in the example output. + until the external IP of the `proxy-public` [service](https://kubernetes.io/docs/concepts/services-networking/service/) is + available. ``` kubectl -n get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' From 7ff31fd5dfe5cf8065bdd71015e4395a8e44764d Mon Sep 17 00:00:00 2001 From: Alexander Dunkel Date: Wed, 3 Aug 2022 10:28:18 +0200 Subject: [PATCH 477/898] Provide long and short form for getting the public proxy IP --- docs/source/jupyterhub/installation.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 6a4ebe0f5f..87cec9d2e9 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -128,10 +128,20 @@ can try with `nano config.yaml`. proxy-7cb9bc4cc-9bdlp 1/1 Running 0 37s ``` -5. Find the IP we can use to access the JupyterHub. Run the following command - until the external IP of the `proxy-public` [service](https://kubernetes.io/docs/concepts/services-networking/service/) is - available. +5. Find the IP we can use to access the JupyterHub. Run the following + command until the `EXTERNAL-IP` of the `proxy-public` [service](https://kubernetes.io/docs/concepts/services-networking/service/) + is available like in the example output. + ``` + kubectl --namespace= get svc proxy-public + ``` + + ``` + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + proxy-public LoadBalancer 10.51.248.230 104.196.41.97 80:31916/TCP 1m + ``` + + Or, use the short form: ``` kubectl -n get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' ``` From ba9928e819d4f6951d63dc558b33e86b690502e7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 08:31:20 +0000 Subject: [PATCH 478/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/jupyterhub/installation.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 87cec9d2e9..8c9e46aa64 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -128,7 +128,7 @@ can try with `nano config.yaml`. proxy-7cb9bc4cc-9bdlp 1/1 Running 0 37s ``` -5. Find the IP we can use to access the JupyterHub. Run the following +5. Find the IP we can use to access the JupyterHub. Run the following command until the `EXTERNAL-IP` of the `proxy-public` [service](https://kubernetes.io/docs/concepts/services-networking/service/) is available like in the example output. @@ -140,8 +140,9 @@ can try with `nano config.yaml`. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE proxy-public LoadBalancer 10.51.248.230 104.196.41.97 80:31916/TCP 1m ``` - + Or, use the short form: + ``` kubectl -n get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' ``` From 2e28c785a7111684379509655f7f7261b617eade Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Aug 2022 05:02:59 +0000 Subject: [PATCH 479/898] build(deps): bump aquasecurity/trivy-action from 0.6.1 to 0.6.2 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.6.1 to 0.6.2. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/81b9a6f5abb1047d697af7a3ca18c13f55a97315...cb606dfdb0d2b3698ace62192088ef4f5360b24f) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index e7938835de..11c2d9afdf 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 + uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 + uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@81b9a6f5abb1047d697af7a3ca18c13f55a97315 + uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f with: image-ref: rebuilt-image format: table From 87b3b40154717c9ddce5d23700b0b71e2dc6eb5e Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 8 Aug 2022 05:22:24 +0000 Subject: [PATCH 480/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 976ae04aa8..edbf6cc050 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -2,7 +2,7 @@ # --------------- FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-07-11_05:24:45 +# VULN_SCAN_TIME=2022-08-08_05:22:22 WORKDIR /build-stage From db6e90b8d140550eeeef4cb8ee0b01603ef305d9 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 8 Aug 2022 07:29:56 +0000 Subject: [PATCH 481/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 9d283b91be..3861bda4fc 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -42,7 +42,7 @@ escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator -frozenlist==1.3.0 +frozenlist==1.3.1 # via # aiohttp # aiosignal @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.9.0 +jsonschema==4.9.1 # via # jupyter-telemetry # oauthenticator From 9ba98c53ed1ff4aa6bdf2c3749766d125b392a2b Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 8 Aug 2022 07:54:07 +0000 Subject: [PATCH 482/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 8bb594d464..10b61e0a1a 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ FROM jupyter/base-notebook:latest # https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile # Built from... Ubuntu 20.04 -# VULN_SCAN_TIME=2022-07-18_05:31:38 +# VULN_SCAN_TIME=2022-08-08_07:54:05 USER root RUN apt-get update \ From 24c1669080e1911a9cfb9cbd1a02b1ef4b646958 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Aug 2022 11:11:17 +0200 Subject: [PATCH 483/898] ci: get latest version, including pre-releases --- .github/workflows/watch-dependencies.yaml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 0bd5f244ee..138b0eea7e 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -141,6 +141,9 @@ jobs: with: python-version: "3.9" + - name: Install Python dependencies + run: pip install requests + - name: Get images/hub/requirements.in pinned version of jupyterhub id: local run: | @@ -149,12 +152,16 @@ jobs: - name: Get latest version of jupyterhub id: latest + shell: python run: | - latest_version=$( - curl -s https://pypi.org/pypi/jupyterhub/json \ - | jq -r .info.version - ) - echo "::set-output name=version::$latest_version" + import requests + + request = requests.get("https://pypi.org/pypi/jupyterhub/json") + data = request.json() + releases = data["releases"] + latest_version = list(releases.keys())[-1] + + print(f"::set-output name=version::{latest_version}") - name: Update pinned version of jupyterhub if: steps.local.outputs.version != steps.latest.outputs.version From 9ecbb0d6ddd9879a26c07e5b27e6db64107d0707 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Aug 2022 11:20:44 +0200 Subject: [PATCH 484/898] ci: refreeze hub/images/requirements.txt after bumping jupyterhub --- .github/workflows/watch-dependencies.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 138b0eea7e..d88fd62769 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -170,6 +170,22 @@ jobs: sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml + - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in + if: steps.local.outputs.version != steps.latest.outputs.version + # IMPORTANT: This run segment is duplicated in this workflow file across + # two separate jobs, any update here should be made in the + # other job as well. + # + run: | + cd images/hub + docker run --rm \ + --env=CUSTOM_COMPILE_COMMAND='Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml' \ + --volume=$PWD:/io \ + --workdir=/io \ + --user=root \ + python:3.9-bullseye \ + sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + - name: git diff if: steps.local.outputs.version != steps.latest.outputs.version run: git --no-pager diff --color=always @@ -211,6 +227,10 @@ jobs: # but we workaround them by by omitting the `-slim` part from the image # in the command below. # + # IMPORTANT: This run segment is duplicated in this workflow file across + # two separate jobs, any update here should be made in the + # other job as well. + # run: | cd images/hub docker run --rm \ From 256c1baa504657f44c22258abe9966ab2bc00d4a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Aug 2022 11:25:48 +0200 Subject: [PATCH 485/898] ci: add comment next to version pinning to guide updates --- images/hub/requirements.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 99e982df97..73987da962 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -5,7 +5,8 @@ # that will also update the jupyterhub version if needed. # README.md file. -# JupyterHub itself +# JupyterHub itself, update this version pinning by running the workflow +# mentioned above. jupyterhub==3.0.0b1 ## Authenticators From 3ec8094a3c4b12ec12797fba07d5de7b709f13ca Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 8 Aug 2022 14:12:44 +0200 Subject: [PATCH 486/898] ci: handle version sorting with Min's suggestion --- .github/workflows/watch-dependencies.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index d88fd62769..6d9a91b8cf 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -142,7 +142,7 @@ jobs: python-version: "3.9" - name: Install Python dependencies - run: pip install requests + run: pip install packaging requests - name: Get images/hub/requirements.in pinned version of jupyterhub id: local @@ -154,12 +154,13 @@ jobs: id: latest shell: python run: | + import packaging.version import requests request = requests.get("https://pypi.org/pypi/jupyterhub/json") data = request.json() releases = data["releases"] - latest_version = list(releases.keys())[-1] + latest_version = sorted(releases.keys(), key=packaging.version.Version)[-1] print(f"::set-output name=version::{latest_version}") From f9da009830052ccd1b85526f7c8f65d9b5d633bf Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 12 Aug 2022 05:13:25 +0000 Subject: [PATCH 487/898] Update library/traefik version from v2.8.1 to v2.8.2 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index a35bb5a46c..ff4e8bb92f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.1" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.2" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From c8ae476bcbd7948b6672e6a6d5b267f228f30ecc Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 12 Aug 2022 15:39:14 +0000 Subject: [PATCH 488/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3861bda4fc..e33ca301fd 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -179,7 +179,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.39 +sqlalchemy==1.4.40 # via # alembic # jupyterhub From 5ed647bb3009a2a06958ae15e831efbe1e49a86e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 12 Aug 2022 23:53:14 +0200 Subject: [PATCH 489/898] ci: remove no longer needed workaround for action-k3s-helm --- .github/workflows/test-chart.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index d9525b5833..4b790819a3 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -128,14 +128,10 @@ jobs: # k3s-version: https://github.com/rancher/k3s/tags # k3s-channel: https://update.k3s.io/v1-release/channels # - # NOTE: we workaround a bug for k3s 1.22+ by passing `extra-setup-args: --egress-selector-mode=disabled` - # include: - k3s-channel: latest - k3s-extra-setup-args: --egress-selector-mode=disabled test: install - k3s-channel: stable - k3s-extra-setup-args: --egress-selector-mode=disabled test: install - k3s-channel: v1.21 # also test prePuller.hook test: install @@ -166,7 +162,6 @@ jobs: # https://jupyterhub.github.io/helm-chart/info.json # - k3s-channel: v1.23 - k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade upgrade-from: stable upgrade-from-extra-args: >- @@ -180,7 +175,6 @@ jobs: --set singleuser.storage.type=dynamic create-k8s-test-resources: true - k3s-channel: v1.23 - k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade upgrade-from: dev upgrade-from-extra-args: >- @@ -191,7 +185,6 @@ jobs: --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic - k3s-channel: v1.22 - k3s-extra-setup-args: --egress-selector-mode=disabled test: upgrade # We're testing hub.db.upgrade with PostgreSQL so this version must be old # enough to require a DB upgrade @@ -237,7 +230,6 @@ jobs: metrics-enabled: false traefik-enabled: false docker-enabled: true - extra-setup-args: "${{ matrix.k3s-extra-setup-args }}" - uses: actions/setup-python@v4 with: From 93264eadf97b259f7c2746e964a55a730cd856d6 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 13 Aug 2022 00:42:10 +0000 Subject: [PATCH 490/898] Update library/traefik version from v2.8.2 to v2.8.3 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index ff4e8bb92f..effd10af35 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.2" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.3" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From ddfc0ba88dd15dcb6127f65688ea6868b36919cb Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 15 Aug 2022 05:35:26 +0000 Subject: [PATCH 491/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 5975d9cdc8..aa47f065bc 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-07-25_05:26:46 +# VULN_SCAN_TIME=2022-08-15_05:35:25 RUN apk add --no-cache iptables From d3ee6ecde95a99da7735372afbb7d9133fd4da40 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 15 Aug 2022 05:35:58 +0000 Subject: [PATCH 492/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index ddd5a4e50f..8832e35484 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.8-alpine -# VULN_SCAN_TIME=2022-07-25_05:27:18 +# VULN_SCAN_TIME=2022-08-15_05:35:56 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 21066ccb8be3b277eaaf45e001c63dbfd736e190 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 11:17:08 +0200 Subject: [PATCH 493/898] docs: adjust legacy redirection to new destination --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 4b647f5734..7cc5bd0ba5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -138,7 +138,7 @@ def _get_git_ref_from_chartpress_based_version(version): "redhat/step-zero-openshift": "kubernetes/redhat/step-zero-openshift", "amazon/step-zero-aws-eks": "kubernetes/amazon/step-zero-aws-eks", "amazon/step-zero-aws": "kubernetes/amazon/step-zero-aws", - "microsoft/step-zero-azure-autoscale": "kubernetes/microsoft/step-zero-azure-autoscale", + "microsoft/step-zero-azure-autoscale": "kubernetes/microsoft/step-zero-azure", "microsoft/step-zero-azure": "kubernetes/microsoft/step-zero-azure", "google/step-zero-gcp": "kubernetes/google/step-zero-gcp", "create-k8s-cluster": "kubernetes/setup-kubernetes", From 8c17e13f4fa43f905c5bf66df0151142e8f8774d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 11:28:45 +0200 Subject: [PATCH 494/898] ci: add workaround for k3s in circleci tests as well --- .circleci/config.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d342de905d..b8acaa487d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ jobs: # https://circleci.com/docs/2.0/arm-resources/#using-arm-resources test-arm: machine: - image: ubuntu-2004:202101-01 + image: ubuntu-2004:2022.04.1 resource_class: arm.medium steps: - checkout @@ -18,12 +18,22 @@ jobs: name: Check architecture - run: + # NOTE: we can't use k3s 1.24 and --docker unless we also install for + # example cri-dockerd as done in + # https://github.com/jupyterhub/action-k3s-helm. + # + # NOTE: we declare --egress-selector-mode=disabled to workaround + # intermittent issues in k3s introduced as a regression in k3s + # 1.22.10, 1.23.7, and 1.24.0. This is tracked in + # https://github.com/k3s-io/k3s/issues/5633. + # command: >- curl -sfL https://get.k3s.io | - INSTALL_K3S_CHANNEL=v1.22 sh -s - + INSTALL_K3S_CHANNEL=v1.23 sh -s - --disable metrics-server --disable traefik --docker + --egress-selector-mode=disabled name: Install K3S - run: From 1fb30dff68db2c9fb4cd505b002684c1de068440 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 12:21:41 +0200 Subject: [PATCH 495/898] Consistent indentation and use of full resource names and flags --- docs/source/jupyterhub/installation.md | 4 ++-- docs/source/kubernetes/microsoft/step-zero-azure.md | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index 8c9e46aa64..e61eabfc34 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -133,7 +133,7 @@ can try with `nano config.yaml`. is available like in the example output. ``` - kubectl --namespace= get svc proxy-public + kubectl --namespace get service proxy-public ``` ``` @@ -144,7 +144,7 @@ can try with `nano config.yaml`. Or, use the short form: ``` - kubectl -n get svc proxy-public -o jsonpath='{.status.loadBalancer.ingress[].ip}' + kubectl --namespace get service proxy-public --output jsonpath='{.status.loadBalancer.ingress[].ip}' ``` 6. To use JupyterHub, enter the external IP for the `proxy-public` service in diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index cd7871b7b9..f7aea7b001 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -246,12 +246,12 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --query [0].name \ --output tsv) az aks nodepool update \ - --name $SP_POOLNAME \ - --cluster-name \ - --resource-group \ - --enable-cluster-autoscaler \ - --min-count \ - --max-count + --name $SP_POOLNAME \ + --cluster-name \ + --resource-group \ + --enable-cluster-autoscaler \ + --min-count \ + --max-count ``` or update the parameters with `--update-cluster-autoscaler`. From d0c57e561be320d7f6819dc2680f037c895be497 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 12:42:51 +0200 Subject: [PATCH 496/898] Update to reflect recent changes --- docs/source/changelog.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 8471d95da3..11f487c53a 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,7 +14,7 @@ this list should be updated. ## 2.0 -### 2.0.0-beta.1 +### 2.0.0 (currently beta.1) WARNING: This is a beta release of the JupyterHub Helm chart. @@ -25,7 +25,7 @@ Ensure you backup your system and test any upgrades if you plan to use this in p Z2JH 2.0.0 is the first major release since 1.0.0 was released in June 2021, and contains major upgrades to all JupyterHub components. -JupyterHub 2.x includes new RBAC support allowing fine-grained access control to hub services and servers. +JupyterHub 3.x includes new RBAC support allowing fine-grained access control to hub services and servers. JupyterLab, the next-generation Notebook interface, is now the default interface seen by users. This brings a full development environment with a large number of extensions developed by the Jupyter community. @@ -78,7 +78,7 @@ the changelog. | Dependency | Version in 1.2.0 | Version in 2.0.0 | Changelog link | Note | | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | -| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 1.4.2 | 2.3.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 1.4.2 | 3.0.0b1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | | [kubespawner](https://github.com/jupyterhub/kubespawner) | 1.1.0 | 4.1.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | | [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 14.2.0 | 15.0.1 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | @@ -86,13 +86,14 @@ the changelog. | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 0.0.7 | 1.0.5 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | | [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.0 | 4.5.1 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | -| [traefik](https://github.com/traefik/traefik) | v2.4.11 | v2.8.1 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [traefik](https://github.com/traefik/traefik) | v2.4.11 | v2.8.3 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | | [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.19.13 | v1.23.9 | - | Run in the `user-scheduler` pod(s) | For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt) file and use its git history to see what changes between tagged versions. #### New features added +- Add `labels` config for `scheduling.userScheduler`, `scheduling.userPlaceholder`, and `prePuller` [#2791](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2791) ([@ruben-rodriguez](https://github.com/ruben-rodriguez)) - Add scheduling.userScheduler.annotations [#2763](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2763) ([@joncotton](https://github.com/joncotton)) - Add scheduling.userPlaceholder.annotations [#2762](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2762) ([@joncotton](https://github.com/joncotton)) - Add `.create` and `.name` to serviceAccount config, and decouple `rbac.enable` from the service accounts [#2736](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2736) ([@dingobar](https://github.com/dingobar), [@consideRatio](https://github.com/consideRatio), [@desaintmartin](https://github.com/desaintmartin)) @@ -104,13 +105,13 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Enhancements made -- Add `labels` config for `scheduling.userScheduler`, `scheduling.userPlaceholder`, and `prePuller` [#2791](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2791) ([@ruben-rodriguez](https://github.com/ruben-rodriguez)) - Enable parent chart's (binderhub etc) to use imagePullSecrets helper [#2546](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2546) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) - Add hub.loadRoles configuration [#2405](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2405) ([@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) - Add ingress.ingressClassName config option [#2403](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2403) ([@consideRatio](https://github.com/consideRatio)) #### Bugs fixed +- Fix user-scheduler backward compatibility for AWS EKS [#2807](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2807) ([@a3626a](https://github.com/a3626a)) - Fix for PDBs in k8s 1.20 [#2727](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2727) ([@consideRatio](https://github.com/consideRatio), [@geoffo-dev](https://github.com/geoffo-dev)) - Enable image-puller pods to evict user-placeholder pods [#2681](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2681) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda), [@a3626a](https://github.com/a3626a)) - Fix failure to respect proxy.secretSync.resources configuration [#2628](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2628) ([@jhowton-restor3d](https://github.com/jhowton-restor3d), [@consideRatio](https://github.com/consideRatio)) @@ -123,8 +124,8 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Maintenance and upkeep improvements +- Restore jupyterhub-singleuser as the default command [#2820](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2820) ([@minrk](https://github.com/minrk)) - Adjust kerning on large JupyterHub in NOTES.txt [#2787](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2787) ([@manics](https://github.com/manics)) -- 2.0.0 upgrade guide [#2779](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2779) ([@manics](https://github.com/manics)) - hub image: remove wheel building aarch64 workaround for pycryptodomex [#2766](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2766) ([@consideRatio](https://github.com/consideRatio)) - hub image: downgrade to ltiauthenticator 1.2.0 [#2741](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2741) ([@consideRatio](https://github.com/consideRatio)) - breaking, maint: replace rbac.enabled with rbac.create [#2739](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2739) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) @@ -156,8 +157,14 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Documentation improvements +- Update docs to reflect Azure 2022 process [#2823](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2823) ([@Sieboldianus](https://github.com/Sieboldianus)) +- Fix step-zero-ovh 404 [#2821](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2821) ([@manics](https://github.com/manics)) +- docs: fix misc link redirects [#2816](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2816) ([@consideRatio](https://github.com/consideRatio)) +- Replace jhub with for consistency [#2815](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2815) ([@rickwierenga](https://github.com/rickwierenga)) +- Add breaking KubeSpawner changes to upgrade-1-to-2 [#2810](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2810) ([@manics](https://github.com/manics)) - docs: fix most broken links in changelog [#2790](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2790) ([@consideRatio](https://github.com/consideRatio)) - Fix `redirected permanently` linkcheck apart from changelog [#2789](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2789) ([@manics](https://github.com/manics)) +- 2.0.0 upgrade guide [#2779](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2779) ([@manics](https://github.com/manics)) - docs: remove broken links and use https over http in a few [#2775](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2775) ([@consideRatio](https://github.com/consideRatio)) - docs: transition rST based glossary to MyST [#2770](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2770) ([@consideRatio](https://github.com/consideRatio)) - docs: move changelog from pure markdown to sphinx based docs [#2769](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2769) ([@consideRatio](https://github.com/consideRatio)) @@ -226,11 +233,9 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Contributors to this release -## Contributors to this release - -([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2021-08-25&to=2022-07-14&type=c)) +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2021-08-25&to=2022-08-16&type=c)) -[@abdelq](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aabdelq+updated%3A2021-08-25..2022-07-14&type=Issues) | [@Adam-Antios](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAdam-Antios+updated%3A2021-08-25..2022-07-14&type=Issues) | [@alex-g-tejada](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalex-g-tejada+updated%3A2021-08-25..2022-07-14&type=Issues) | [@AlexChung1995](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAlexChung1995+updated%3A2021-08-25..2022-07-14&type=Issues) | [@BertR](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ABertR+updated%3A2021-08-25..2022-07-14&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2021-08-25..2022-07-14&type=Issues) | [@bjornarfjelldal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornarfjelldal+updated%3A2021-08-25..2022-07-14&type=Issues) | [@chancez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achancez+updated%3A2021-08-25..2022-07-14&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2021-08-25..2022-07-14&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-08-25..2022-07-14&type=Issues) | [@cslovell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acslovell+updated%3A2021-08-25..2022-07-14&type=Issues) | [@delamart](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adelamart+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dhirschfeld](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adhirschfeld+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dingobar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adingobar+updated%3A2021-08-25..2022-07-14&type=Issues) | [@dmigo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Admigo+updated%3A2021-08-25..2022-07-14&type=Issues) | [@Economax](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AEconomax+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ellisonbg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aellisonbg+updated%3A2021-08-25..2022-07-14&type=Issues) | [@GeorgianaElena](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGeorgianaElena+updated%3A2021-08-25..2022-07-14&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2021-08-25..2022-07-14&type=Issues) | [@gregingenii](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agregingenii+updated%3A2021-08-25..2022-07-14&type=Issues) | [@gsemet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agsemet+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jdmcbr](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajdmcbr+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jhowton-restor3d](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajhowton-restor3d+updated%3A2021-08-25..2022-07-14&type=Issues) | [@joncotton](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoncotton+updated%3A2021-08-25..2022-07-14&type=Issues) | [@joraff](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoraff+updated%3A2021-08-25..2022-07-14&type=Issues) | [@jupyterhub-bot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2021-08-25..2022-07-14&type=Issues) | [@kindomLee](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AkindomLee+updated%3A2021-08-25..2022-07-14&type=Issues) | [@lud0v1c](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Alud0v1c+updated%3A2021-08-25..2022-07-14&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-08-25..2022-07-14&type=Issues) | [@matthew-brett](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amatthew-brett+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mcberma](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amcberma+updated%3A2021-08-25..2022-07-14&type=Issues) | [@meeseeksmachine](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ameeseeksmachine+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mike-matera](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amike-matera+updated%3A2021-08-25..2022-07-14&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-08-25..2022-07-14&type=Issues) | [@MLobo1997](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AMLobo1997+updated%3A2021-08-25..2022-07-14&type=Issues) | [@mriedem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amriedem+updated%3A2021-08-25..2022-07-14&type=Issues) | [@nreith](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Anreith+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ostapkonst](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aostapkonst+updated%3A2021-08-25..2022-07-14&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apre-commit-ci+updated%3A2021-08-25..2022-07-14&type=Issues) | [@pvanliefland](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apvanliefland+updated%3A2021-08-25..2022-07-14&type=Issues) | [@raybellwaves](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Araybellwaves+updated%3A2021-08-25..2022-07-14&type=Issues) | [@remche](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aremche+updated%3A2021-08-25..2022-07-14&type=Issues) | [@ruben-rodriguez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aruben-rodriguez+updated%3A2021-08-25..2022-07-14&type=Issues) | [@sgibson91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgibson91+updated%3A2021-08-25..2022-07-14&type=Issues) | [@snickell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asnickell+updated%3A2021-08-25..2022-07-14&type=Issues) | [@srggrs](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asrggrs+updated%3A2021-08-25..2022-07-14&type=Issues) | [@sunu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asunu+updated%3A2021-08-25..2022-07-14&type=Issues) | [@support](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asupport+updated%3A2021-08-25..2022-07-14&type=Issues) | [@theomper](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atheomper+updated%3A2021-08-25..2022-07-14&type=Issues) | [@timotk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atimotk+updated%3A2021-08-25..2022-07-14&type=Issues) | [@welcome](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awelcome+updated%3A2021-08-25..2022-07-14&type=Issues) | [@willingc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awillingc+updated%3A2021-08-25..2022-07-14&type=Issues) | [@wyphan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awyphan+updated%3A2021-08-25..2022-07-14&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2021-08-25..2022-07-14&type=Issues) +[@a3626a](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aa3626a+updated%3A2021-08-25..2022-08-16&type=Issues) | [@abdelq](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aabdelq+updated%3A2021-08-25..2022-08-16&type=Issues) | [@Adam-Antios](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAdam-Antios+updated%3A2021-08-25..2022-08-16&type=Issues) | [@alex-g-tejada](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalex-g-tejada+updated%3A2021-08-25..2022-08-16&type=Issues) | [@AlexChung1995](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AAlexChung1995+updated%3A2021-08-25..2022-08-16&type=Issues) | [@BertR](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ABertR+updated%3A2021-08-25..2022-08-16&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2021-08-25..2022-08-16&type=Issues) | [@bjornarfjelldal](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornarfjelldal+updated%3A2021-08-25..2022-08-16&type=Issues) | [@chancez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Achancez+updated%3A2021-08-25..2022-08-16&type=Issues) | [@choldgraf](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2021-08-25..2022-08-16&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2021-08-25..2022-08-16&type=Issues) | [@cslovell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acslovell+updated%3A2021-08-25..2022-08-16&type=Issues) | [@delamart](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adelamart+updated%3A2021-08-25..2022-08-16&type=Issues) | [@dependabot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adependabot+updated%3A2021-08-25..2022-08-16&type=Issues) | [@dhirschfeld](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adhirschfeld+updated%3A2021-08-25..2022-08-16&type=Issues) | [@dingobar](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adingobar+updated%3A2021-08-25..2022-08-16&type=Issues) | [@dmigo](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Admigo+updated%3A2021-08-25..2022-08-16&type=Issues) | [@Economax](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AEconomax+updated%3A2021-08-25..2022-08-16&type=Issues) | [@ellisonbg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aellisonbg+updated%3A2021-08-25..2022-08-16&type=Issues) | [@GeorgianaElena](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AGeorgianaElena+updated%3A2021-08-25..2022-08-16&type=Issues) | [@gregingenii](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agregingenii+updated%3A2021-08-25..2022-08-16&type=Issues) | [@gsemet](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agsemet+updated%3A2021-08-25..2022-08-16&type=Issues) | [@jdmcbr](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajdmcbr+updated%3A2021-08-25..2022-08-16&type=Issues) | [@jhowton-restor3d](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajhowton-restor3d+updated%3A2021-08-25..2022-08-16&type=Issues) | [@joncotton](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoncotton+updated%3A2021-08-25..2022-08-16&type=Issues) | [@joraff](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajoraff+updated%3A2021-08-25..2022-08-16&type=Issues) | [@jupyterhub-bot](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2021-08-25..2022-08-16&type=Issues) | [@kindomLee](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AkindomLee+updated%3A2021-08-25..2022-08-16&type=Issues) | [@lucianolacurcia](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Alucianolacurcia+updated%3A2021-08-25..2022-08-16&type=Issues) | [@lud0v1c](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Alud0v1c+updated%3A2021-08-25..2022-08-16&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2021-08-25..2022-08-16&type=Issues) | [@matthew-brett](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amatthew-brett+updated%3A2021-08-25..2022-08-16&type=Issues) | [@mcberma](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amcberma+updated%3A2021-08-25..2022-08-16&type=Issues) | [@mgobec](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amgobec+updated%3A2021-08-25..2022-08-16&type=Issues) | [@mike-matera](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amike-matera+updated%3A2021-08-25..2022-08-16&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2021-08-25..2022-08-16&type=Issues) | [@MLobo1997](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AMLobo1997+updated%3A2021-08-25..2022-08-16&type=Issues) | [@mriedem](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amriedem+updated%3A2021-08-25..2022-08-16&type=Issues) | [@nreith](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Anreith+updated%3A2021-08-25..2022-08-16&type=Issues) | [@ostapkonst](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aostapkonst+updated%3A2021-08-25..2022-08-16&type=Issues) | [@pre-commit-ci](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apre-commit-ci+updated%3A2021-08-25..2022-08-16&type=Issues) | [@pvanliefland](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apvanliefland+updated%3A2021-08-25..2022-08-16&type=Issues) | [@raybellwaves](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Araybellwaves+updated%3A2021-08-25..2022-08-16&type=Issues) | [@remche](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aremche+updated%3A2021-08-25..2022-08-16&type=Issues) | [@rickwierenga](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Arickwierenga+updated%3A2021-08-25..2022-08-16&type=Issues) | [@ruben-rodriguez](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aruben-rodriguez+updated%3A2021-08-25..2022-08-16&type=Issues) | [@sgibson91](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asgibson91+updated%3A2021-08-25..2022-08-16&type=Issues) | [@Sieboldianus](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ASieboldianus+updated%3A2021-08-25..2022-08-16&type=Issues) | [@snickell](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asnickell+updated%3A2021-08-25..2022-08-16&type=Issues) | [@srggrs](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asrggrs+updated%3A2021-08-25..2022-08-16&type=Issues) | [@sunu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Asunu+updated%3A2021-08-25..2022-08-16&type=Issues) | [@theomper](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atheomper+updated%3A2021-08-25..2022-08-16&type=Issues) | [@timotk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Atimotk+updated%3A2021-08-25..2022-08-16&type=Issues) | [@willingc](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awillingc+updated%3A2021-08-25..2022-08-16&type=Issues) | [@wyphan](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Awyphan+updated%3A2021-08-25..2022-08-16&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2021-08-25..2022-08-16&type=Issues) ## 1.2 From 422855b5f9fbbb000c398bd37d41e1967b9d877b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 13:00:30 +0200 Subject: [PATCH 497/898] Updates to reflect the use of JupyterHub 3 --- docs/source/administrator/upgrading/upgrade-1-to-2.md | 6 +++--- docs/source/changelog.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index 2857b11f7b..e41004d025 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -49,11 +49,11 @@ and the configuration reference entries under ## JupyterHub 2 and related hub components -Z2JH 2.0.0 upgrades JupyterHub to the 2.\* series, and also upgrades all hub components. +Z2JH 2.0.0 upgrades from JupyterHub 1 directly to JupyterHub 3, and also upgrades all hub components. If you are using any custom JupyterHub services, addons, API integrations, or extra configuration, you should review the breaking changes in the -[JupyterHub 2.x changelog](https://jupyterhub.readthedocs.io/en/2.3.1/changelog.html). +major releases of JupyterHub 2 and 3 in the [JupyterHub changelog](https://jupyterhub.readthedocs.io/en/latest/changelog.html). -JupyterHub 2 updates the database schema, which means a migration takes place when you upgrade JupyterHub. +JupyterHub 2 and 3 updates the database schema, which means a migration takes place when you upgrade JupyterHub. Z2JH automatically handles the upgrade if you are using sqlite (`hub.db.type = 'sqlite-pvc'`, the default), but it may not be possible to downgrade to older releases after this. When using sqlite, JupyterHub automatically creates a backup in the `hub-db` volume, which can be restored manually if you need to downgrade. diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 11f487c53a..4b8d11447b 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -23,9 +23,9 @@ Ensure you backup your system and test any upgrades if you plan to use this in p #### Highlights -Z2JH 2.0.0 is the first major release since 1.0.0 was released in June 2021, and contains major upgrades to all JupyterHub components. +Z2JH 2.0.0 is the first major release since 1.0.0 was released in June 2021, and contains major upgrades to all JupyterHub components, including a jump directly from JupyterHub 1 to JupyterHub 3. -JupyterHub 3.x includes new RBAC support allowing fine-grained access control to hub services and servers. +JupyterHub 2 and 3 includes new RBAC support allowing fine-grained access control to hub services and servers. JupyterLab, the next-generation Notebook interface, is now the default interface seen by users. This brings a full development environment with a large number of extensions developed by the Jupyter community. From e8cc8a1a141f1e442d690ec7dfb420378f0556e4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 16 Aug 2022 13:02:58 +0200 Subject: [PATCH 498/898] docs: remove a todo note --- docs/source/changelog.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 4b8d11447b..0237c8c74d 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -65,15 +65,6 @@ These breaking changes have been made relative to the 1.\* series of Z2JH releas For information on how to update your configuration see the [](administrator/upgrading/upgrade-1-to-2) guide. -#### TODO: link out to relevant informational posts - -TODO: Write a discourse forum post about the following and highlight that via -the changelog. - -- 100+ user-placeholder pods -> node-placeholder pods -- autohttps acme acquisition issues on GKE mitigation - (notable-dependencies-200)= - #### Notable dependencies updated | Dependency | Version in 1.2.0 | Version in 2.0.0 | Changelog link | Note | From 25483c6fa854ffa684bbb1f7c61db9c2841dca2e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Aug 2022 13:28:06 +0200 Subject: [PATCH 499/898] hub image: remove workaround for ruamel.yaml.clib on aarch64 --- images/hub/Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index edbf6cc050..50da22adaf 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -11,9 +11,6 @@ WORKDIR /build-stage # # - https://pypi.org/project/pycurl/#files, no wheels available as of 7.45.1. # See https://github.com/pycurl/pycurl/issues/738. -# - https://pypi.org/project/ruamel.yaml.clib/#files, no aarch64 wheels -# available as of 0.2.6. See -# https://sourceforge.net/p/ruamel-yaml-clib/tickets/4/. # # If you find a dependency here no longer listed in requirements.txt, remove it # from here as well. The more wheels we build here and copy and then install, @@ -23,8 +20,7 @@ WORKDIR /build-stage COPY requirements.txt requirements.txt RUN pip install build \ && pip wheel \ - $(cat requirements.txt | grep "pycurl==") \ - $(cat requirements.txt | grep "ruamel-yaml-clib==") + $(cat requirements.txt | grep "pycurl==") # The final stage From 879ab75dc07619d20c6d817a6e662ebc37de22f9 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 18 Aug 2022 05:31:41 +0000 Subject: [PATCH 500/898] Update kube-scheduler version from v1.23.9 to v1.23.10 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index effd10af35..dc71438440 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.9" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From e7307616d80960bb97969af5db5e8a2b1dcf930b Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Aug 2022 11:13:54 +0200 Subject: [PATCH 501/898] run action-k8s-namespace-report on circleci failure --- .circleci/config.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b8acaa487d..99232c4949 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -73,6 +73,17 @@ jobs: pytest --verbose --color=yes ./tests -m 'not netpol' name: Run tests + - run: + name: k8s namespace report + when: on_fail + command: | + wget https://raw.githubusercontent.com/jupyterhub/action-k8s-namespace-report/v1.1.0/k8s-namespace-report + bash k8s-namespace-report + environment: + NAMESPACE: "" + POD_SELECTOR: "" + IMPORTANT_WORKLOADS: "" + workflows: main: jobs: From 7273ffbc99a152d1173218031dd64d13143f559f Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 17 Aug 2022 11:34:10 +0200 Subject: [PATCH 502/898] switch singleuser base image for arm tests chartpress doesn't support multiple base images, but real deployments won't use the sample image anyway --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 99232c4949..06554d1185 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,6 +50,14 @@ jobs: pip3 install --no-cache-dir -r dev-requirements.txt name: Install dependencies + - run: + name: fix singleuser base image for aarch64 + # Jupyter docker-stacks don't have multi-arch images + # see https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2844 + command: | + sed -i -E s@base-notebook:latest@base-notebook:aarch64-latest@ images/singleuser-sample/Dockerfile + git diff + - run: command: | chartpress From 4faa7aa7805310ad2129c741088f62a51ba13896 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Aug 2022 11:17:25 +0200 Subject: [PATCH 503/898] run action-k8s-namespace-report on circleci failure --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 06554d1185..c9d7d3dc99 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -85,6 +85,7 @@ jobs: name: k8s namespace report when: on_fail command: | + export KUBECONFIG="$HOME/.kube/config" wget https://raw.githubusercontent.com/jupyterhub/action-k8s-namespace-report/v1.1.0/k8s-namespace-report bash k8s-namespace-report environment: From 67b5fff8e1d486d16a14bbd102fd064c8f039381 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Aug 2022 12:00:08 +0200 Subject: [PATCH 504/898] build our own singleuser-sample image - start from python:3.9-bullseye-slim - uses pip-compile, just like hub - consolidate pip-compile refreeze into `ci/refreeze` (simplifies workflows, reduces duplication, and allows local calls) - get tini from apt --- .github/workflows/watch-dependencies.yaml | 53 +--- ci/refreeze | 19 ++ images/hub/Dockerfile | 8 +- images/hub/requirements.txt | 4 +- images/singleuser-sample/Dockerfile | 55 +++- images/singleuser-sample/requirements.in | 18 ++ images/singleuser-sample/requirements.txt | 328 +++++++++++++++++++++- 7 files changed, 421 insertions(+), 64 deletions(-) create mode 100755 ci/refreeze create mode 100644 images/singleuser-sample/requirements.in diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 6d9a91b8cf..6aebc6dacb 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -4,9 +4,9 @@ # - Watch multiple images tags referenced in values.yaml to match the latest # image tag. # -# - Watch the jupyterhub pinning in images/hub/requirements.in to match the +# - Watch the jupyterhub pinning in images/*/requirements.in to match the # latest jupyterhub version available on PyPI, and if doing this, also -# refreeze images/hub/requirements.txt. +# refreeze images/*/requirements.txt. # # About environment: watch-dependencies # @@ -20,9 +20,7 @@ name: Watch dependencies on: push: paths: - - "images/hub/Dockerfile" - - "images/hub/requirements.in" - - "images/hub/requirements.txt" + - "images/*/requirements.in" - ".github/workflows/watch-dependencies.yaml" branches: ["main"] schedule: @@ -167,25 +165,14 @@ jobs: - name: Update pinned version of jupyterhub if: steps.local.outputs.version != steps.latest.outputs.version run: | - sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/hub/requirements.in - sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/singleuser-sample/requirements.txt + for img in hub singleuser-sample; do + sed --in-place 's/jupyterhub==${{ steps.local.outputs.version }}/jupyterhub==${{ steps.latest.outputs.version }}/g' images/$img/requirements.in + done sed --in-place 's/appVersion: "${{ steps.local.outputs.version }}"/appVersion: "${{ steps.latest.outputs.version }}"/g' jupyterhub/Chart.yaml - - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in + - name: Refreeze images/*/requirements.txt based on images/*/requirements.in if: steps.local.outputs.version != steps.latest.outputs.version - # IMPORTANT: This run segment is duplicated in this workflow file across - # two separate jobs, any update here should be made in the - # other job as well. - # - run: | - cd images/hub - docker run --rm \ - --env=CUSTOM_COMPILE_COMMAND='Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml' \ - --volume=$PWD:/io \ - --workdir=/io \ - --user=root \ - python:3.9-bullseye \ - sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + run: ci/refreeze - name: git diff if: steps.local.outputs.version != steps.latest.outputs.version @@ -219,28 +206,8 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Refreeze images/hub/requirements.txt based on images/hub/requirements.in - # Because `pip-compile` resolves `requirements.txt` with the current - # Python for the current platform, it should be run on the same Python - # version and platform as our Dockerfile. - # - # Note that as of 2022-05-29, `pip-compile` has issues with `pycurl`, - # but we workaround them by by omitting the `-slim` part from the image - # in the command below. - # - # IMPORTANT: This run segment is duplicated in this workflow file across - # two separate jobs, any update here should be made in the - # other job as well. - # - run: | - cd images/hub - docker run --rm \ - --env=CUSTOM_COMPILE_COMMAND='Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml' \ - --volume=$PWD:/io \ - --workdir=/io \ - --user=root \ - python:3.9-bullseye \ - sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + - name: Refreeze images/*/requirements.txt based on images/*/requirements.in + run: ci/refreeze - name: git diff run: git --no-pager diff --color=always diff --git a/ci/refreeze b/ci/refreeze new file mode 100755 index 0000000000..5ba130cee6 --- /dev/null +++ b/ci/refreeze @@ -0,0 +1,19 @@ +#!/bin/bash +set -xeuo pipefail +IMAGES=${1:-images/hub images/singleuser-sample} + +# Because `pip-compile` resolves `requirements.txt` with the current +# Python for the current platform, it should be run on the same Python +# version and platform as our Dockerfile. + +for img in ${IMAGES}; do + pushd "$img" + docker run --rm \ + --env=CUSTOM_COMPILE_COMMAND='Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml' \ + --volume="$PWD:/io" \ + --workdir=/io \ + --user=root \ + python:3.9-bullseye \ + sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + popd +done diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 50da22adaf..09560ebba1 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -54,13 +54,9 @@ RUN apt-get update && \ libcurl4 \ # requirement for using a local sqlite database sqlite3 \ + tini \ && rm -rf /var/lib/apt/lists/* -RUN if [ "$(uname -m)" = x86_64 ]; then ARCH=amd64; fi; \ - if [ "$(uname -m)" = aarch64 ]; then ARCH=arm64; fi; \ - curl -sSLo /tini "https://github.com/krallin/tini/releases/download/v0.19.0/tini-$ARCH" \ - && chmod +x /tini - COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ COPY requirements.txt /tmp/requirements.txt RUN pip install --no-cache-dir \ @@ -72,5 +68,5 @@ RUN chown ${NB_USER}:${NB_USER} /srv/jupyterhub USER ${NB_USER} EXPOSE 8081 -ENTRYPOINT ["/tini", "--"] +ENTRYPOINT ["tini", "--"] CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e33ca301fd..bcfda0d2d0 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -58,7 +58,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.9.1 +jsonschema==4.11.0 # via # jupyter-telemetry # oauthenticator @@ -90,7 +90,7 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes-asyncio==24.2.0 +kubernetes-asyncio==24.2.1 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 10b61e0a1a..021ff527aa 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,26 +1,61 @@ -FROM jupyter/base-notebook:latest -# Built from... https://hub.docker.com/r/jupyter/base-notebook/ -# https://github.com/jupyter/docker-stacks/blob/HEAD/base-notebook/Dockerfile -# Built from... Ubuntu 20.04 +# The build stage +# --------------- +FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-08-08_07:54:05 +# VULN_SCAN_TIME= + +WORKDIR /build-stage + +# Build wheels for packages that require gcc or other build dependencies and +# lack wheels either for amd64 or aarch64. +COPY requirements.txt requirements.txt +RUN pip wheel -r requirements.txt + +# The final stage +# --------------- +FROM python:3.9-slim-bullseye + +# VULN_SCAN_TIME= + +ENV DEBIAN_FRONTEND=noninteractive \ + LANG=en_US.UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANGUAGE=en_US.UTF-8 \ + NB_USER=jovyan \ + NB_UID=1000 \ + HOME=/home/jovyan USER root RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ + ca-certificates \ dnsutils \ git \ + locales \ iputils-ping \ - && rm -rf /var/lib/apt/lists/* -USER $NB_USER + tini \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ + && locale-gen -COPY requirements.txt /tmp/requirements.txt -RUN pip install --no-cache-dir \ - -r /tmp/requirements.txt +COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ +RUN pip install --no-cache-dir /tmp/pre-built-wheels/*.whl # nbgitpuller is installed in requirements.txt for demo purposes, and this # enables it to function. RUN jupyter serverextension enable --py nbgitpuller --sys-prefix # conda/pip/apt install additional packages here, if desired. + +RUN adduser --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} + +USER $NB_USER + +ENTRYPOINT ["tini", "--"] diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in new file mode 100644 index 0000000000..a5c149b7de --- /dev/null +++ b/images/singleuser-sample/requirements.in @@ -0,0 +1,18 @@ +# This file is the input to requirements.txt, +# which is a frozen version of this. To update +# requirements.txt, use the "Run workflow" button at +# https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml +# that will also update the jupyterhub version if needed. +# README.md file. + +# JupyterHub itself, update this version pinning by running the workflow +# mentioned above. +jupyterhub==3.0.0b1 + +# UI +jupyterlab +nbclassic +retrolab + +# plugins +nbgitpuller diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 11566d4d72..6ab32eaacd 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,327 @@ -# This file is automatically maintained by Dependabot, as configured in -# .github/dependabot.yaml. - +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml +# +alembic==1.8.1 + # via jupyterhub +anyio==3.6.1 + # via jupyter-server +argon2-cffi==21.3.0 + # via + # jupyter-server + # nbclassic + # notebook +argon2-cffi-bindings==21.2.0 + # via argon2-cffi +asttokens==2.0.8 + # via stack-data +async-generator==1.10 + # via jupyterhub +attrs==22.1.0 + # via jsonschema +babel==2.10.3 + # via jupyterlab-server +backcall==0.2.0 + # via ipython +beautifulsoup4==4.11.1 + # via nbconvert +bleach==5.0.1 + # via nbconvert +certifi==2022.6.15 + # via requests +certipy==0.1.3 + # via jupyterhub +cffi==1.15.1 + # via + # argon2-cffi-bindings + # cryptography +charset-normalizer==2.1.0 + # via requests +cryptography==37.0.4 + # via pyopenssl +debugpy==1.6.3 + # via ipykernel +decorator==5.1.1 + # via ipython +defusedxml==0.7.1 + # via nbconvert +entrypoints==0.4 + # via + # jupyter-client + # nbconvert +executing==0.10.0 + # via stack-data +fastjsonschema==2.16.1 + # via nbformat +greenlet==1.1.2 + # via sqlalchemy +idna==3.3 + # via + # anyio + # requests +importlib-metadata==4.12.0 + # via + # jupyterhub + # jupyterlab-server +ipykernel==6.15.1 + # via + # nbclassic + # notebook +ipython==8.4.0 + # via + # ipykernel + # jupyterlab +ipython-genutils==0.2.0 + # via + # nbclassic + # notebook +jedi==0.18.1 + # via ipython +jinja2==3.1.2 + # via + # jupyter-server + # jupyterhub + # jupyterlab + # jupyterlab-server + # nbclassic + # nbconvert + # notebook +json5==0.9.9 + # via jupyterlab-server +jsonschema==4.11.0 + # via + # jupyter-telemetry + # jupyterlab-server + # nbformat +jupyter-client==7.3.4 + # via + # ipykernel + # jupyter-server + # nbclassic + # nbclient + # notebook +jupyter-core==4.11.1 + # via + # jupyter-client + # jupyter-server + # jupyterlab + # nbclassic + # nbconvert + # nbformat + # notebook +jupyter-server==1.18.1 + # via + # -r requirements.in + # jupyterlab + # jupyterlab-server + # nbclassic + # nbgitpuller + # notebook-shim + # retrolab +jupyter-telemetry==0.1.0 + # via jupyterhub jupyterhub==3.0.0b1 + # via -r requirements.in +jupyterlab==3.4.5 + # via + # -r requirements.in + # retrolab +jupyterlab-pygments==0.2.2 + # via nbconvert +jupyterlab-server==2.15.0 + # via + # jupyterlab + # retrolab +lxml==4.9.1 + # via nbconvert +mako==1.2.1 + # via alembic +markupsafe==2.1.1 + # via + # jinja2 + # mako + # nbconvert +matplotlib-inline==0.1.6 + # via + # ipykernel + # ipython +mistune==0.8.4 + # via nbconvert +nbclassic==0.4.3 + # via + # -r requirements.in + # jupyterlab + # retrolab +nbclient==0.6.6 + # via nbconvert +nbconvert==6.5.3 + # via + # jupyter-server + # nbclassic + # notebook +nbformat==5.4.0 + # via + # jupyter-server + # nbclassic + # nbclient + # nbconvert + # notebook nbgitpuller==1.1.0 + # via -r requirements.in +nest-asyncio==1.5.5 + # via + # ipykernel + # jupyter-client + # nbclassic + # nbclient + # notebook +notebook==6.4.12 + # via + # jupyterlab + # nbgitpuller +notebook-shim==0.1.0 + # via nbclassic +oauthlib==3.2.0 + # via jupyterhub +packaging==21.3 + # via + # ipykernel + # jupyter-server + # jupyterhub + # jupyterlab + # jupyterlab-server + # nbconvert +pamela==1.0.0 + # via jupyterhub +pandocfilters==1.5.0 + # via nbconvert +parso==0.8.3 + # via jedi +pexpect==4.8.0 + # via ipython +pickleshare==0.7.5 + # via ipython +prometheus-client==0.14.1 + # via + # jupyter-server + # jupyterhub + # nbclassic + # notebook +prompt-toolkit==3.0.30 + # via ipython +psutil==5.9.1 + # via ipykernel +ptyprocess==0.7.0 + # via + # pexpect + # terminado +pure-eval==0.2.2 + # via stack-data +pycparser==2.21 + # via cffi +pygments==2.13.0 + # via + # ipython + # nbconvert +pyopenssl==22.0.0 + # via certipy +pyparsing==3.0.9 + # via packaging +pyrsistent==0.18.1 + # via jsonschema +python-dateutil==2.8.2 + # via + # jupyter-client + # jupyterhub +python-json-logger==2.0.4 + # via jupyter-telemetry +pytz==2022.2.1 + # via babel +pyzmq==23.2.1 + # via + # ipykernel + # jupyter-client + # jupyter-server + # nbclassic + # notebook +requests==2.28.1 + # via + # jupyterhub + # jupyterlab-server +retrolab==0.3.21 + # via -r requirements.in +ruamel-yaml==0.17.21 + # via jupyter-telemetry +ruamel-yaml-clib==0.2.6 + # via ruamel-yaml +send2trash==1.8.0 + # via + # jupyter-server + # nbclassic + # notebook +six==1.16.0 + # via + # asttokens + # bleach + # python-dateutil +sniffio==1.2.0 + # via anyio +soupsieve==2.3.2.post1 + # via beautifulsoup4 +sqlalchemy==1.4.40 + # via + # alembic + # jupyterhub +stack-data==0.4.0 + # via ipython +terminado==0.15.0 + # via + # jupyter-server + # nbclassic + # notebook +tinycss2==1.1.1 + # via nbconvert +tornado==6.2 + # via + # ipykernel + # jupyter-client + # jupyter-server + # jupyterhub + # jupyterlab + # nbclassic + # nbgitpuller + # notebook + # retrolab + # terminado +traitlets==5.3.0 + # via + # ipykernel + # ipython + # jupyter-client + # jupyter-core + # jupyter-server + # jupyter-telemetry + # jupyterhub + # matplotlib-inline + # nbclassic + # nbclient + # nbconvert + # nbformat + # notebook +urllib3==1.26.11 + # via requests +wcwidth==0.2.5 + # via prompt-toolkit +webencodings==0.5.1 + # via + # bleach + # tinycss2 +websocket-client==1.3.3 + # via jupyter-server +zipp==3.8.1 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools From f1ae708dd07349ba490e645690c47633c75020ec Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 20 Aug 2022 05:14:11 +0000 Subject: [PATCH 505/898] Update jupyterhub/configurable-http-proxy version from 4.5.1 to 4.5.2 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index dc71438440..3ed3ce563e 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -198,7 +198,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.1" # https://github.com/jupyterhub/configurable-http-proxy/releases + tag: "4.5.2" # https://github.com/jupyterhub/configurable-http-proxy/releases pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From 5b6bef0ba4bc7fe8e568ee9071f4554a9f92ef20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 05:02:20 +0000 Subject: [PATCH 506/898] build(deps): bump aquasecurity/trivy-action from 0.6.2 to 0.7.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.6.2 to 0.7.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/cb606dfdb0d2b3698ace62192088ef4f5360b24f...d63413b0a4a4482237085319f7f4a1ce99a8f2ac) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 11c2d9afdf..4112e87749 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f + uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -110,7 +110,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f + uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -169,7 +169,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@cb606dfdb0d2b3698ace62192088ef4f5360b24f + uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac with: image-ref: rebuilt-image format: table From 02d97f89142493777bbc23997726d35f018484ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 05:02:23 +0000 Subject: [PATCH 507/898] build(deps): bump peter-evans/create-pull-request from 4 to 4.1.1 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4 to 4.1.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/v4...18f90432bedd2afd6a825469ffd38aa24712a91d) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 11c2d9afdf..b49da07368 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -203,7 +203,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 6d9a91b8cf..ffb8cd79b7 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -116,7 +116,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -194,7 +194,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@923ad837f191474af6b1721408744feb989a4c27 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -247,7 +247,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@v4 + uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From bdff1146781adc463eca1d9e6782c099b48aa4f2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 25 Aug 2022 19:13:25 +0200 Subject: [PATCH 508/898] Revert workaround for singleuser arm image, no longer needed --- .circleci/config.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9d7d3dc99..bf4b79de35 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,14 +50,6 @@ jobs: pip3 install --no-cache-dir -r dev-requirements.txt name: Install dependencies - - run: - name: fix singleuser base image for aarch64 - # Jupyter docker-stacks don't have multi-arch images - # see https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2844 - command: | - sed -i -E s@base-notebook:latest@base-notebook:aarch64-latest@ images/singleuser-sample/Dockerfile - git diff - - run: command: | chartpress From 2df10abe48406eb14a5db642eb3a1c8d880daf5c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 25 Aug 2022 19:21:44 +0200 Subject: [PATCH 509/898] Remove redundant apt-get clean --- images/singleuser-sample/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 021ff527aa..c232d395b4 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -35,7 +35,6 @@ RUN apt-get update \ locales \ iputils-ping \ tini \ - && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ && locale-gen From 6d8f413ebfb74ab0a1a43dd42e8c1de195cd16bc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 25 Aug 2022 19:22:27 +0200 Subject: [PATCH 510/898] Remove not needed build stage --- images/singleuser-sample/Dockerfile | 32 +++++++---------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index c232d395b4..c9283e36dc 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,18 +1,3 @@ -# The build stage -# --------------- -FROM python:3.9-bullseye as build-stage - -# VULN_SCAN_TIME= - -WORKDIR /build-stage - -# Build wheels for packages that require gcc or other build dependencies and -# lack wheels either for amd64 or aarch64. -COPY requirements.txt requirements.txt -RUN pip wheel -r requirements.txt - -# The final stage -# --------------- FROM python:3.9-slim-bullseye # VULN_SCAN_TIME= @@ -25,7 +10,6 @@ ENV DEBIAN_FRONTEND=noninteractive \ NB_UID=1000 \ HOME=/home/jovyan -USER root RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ @@ -39,15 +23,6 @@ RUN apt-get update \ && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ && locale-gen -COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ -RUN pip install --no-cache-dir /tmp/pre-built-wheels/*.whl - -# nbgitpuller is installed in requirements.txt for demo purposes, and this -# enables it to function. -RUN jupyter serverextension enable --py nbgitpuller --sys-prefix - -# conda/pip/apt install additional packages here, if desired. - RUN adduser --disabled-password \ --gecos "Default user" \ --uid ${NB_UID} \ @@ -57,4 +32,11 @@ RUN adduser --disabled-password \ USER $NB_USER +COPY requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +# nbgitpuller is installed in requirements.txt for demo purposes, and this +# enables it to function. +RUN jupyter serverextension enable --py nbgitpuller --sys-prefix + ENTRYPOINT ["tini", "--"] From de878551a272760f7873a73061e334ce78fe2627 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 26 Aug 2022 09:26:17 +0200 Subject: [PATCH 511/898] Rely on python:3.9 default LANG env variable --- images/hub/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 09560ebba1..d5e0623481 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -30,8 +30,7 @@ FROM python:3.9-slim-bullseye ARG NB_USER=jovyan ARG NB_UID=1000 ARG HOME=/home/jovyan -ENV DEBIAN_FRONTEND=noninteractive \ - LANG=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive RUN adduser --disabled-password \ --gecos "Default user" \ From 788dd8a0cbfe6a4cd134c6b63d6ce3e6576b2d12 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 26 Aug 2022 09:27:35 +0200 Subject: [PATCH 512/898] Minimalize singleuser-sample --- images/singleuser-sample/Dockerfile | 31 +++++++++++------------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index c9283e36dc..00d59dbcb3 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,40 +3,33 @@ FROM python:3.9-slim-bullseye # VULN_SCAN_TIME= ENV DEBIAN_FRONTEND=noninteractive \ - LANG=en_US.UTF-8 \ - LC_ALL=en_US.UTF-8 \ - LANGUAGE=en_US.UTF-8 \ NB_USER=jovyan \ NB_UID=1000 \ HOME=/home/jovyan +RUN adduser --disabled-password \ + --gecos "Default user" \ + --uid ${NB_UID} \ + --home ${HOME} \ + --force-badname \ + ${NB_USER} + RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ ca-certificates \ dnsutils \ git \ - locales \ iputils-ping \ tini \ - && rm -rf /var/lib/apt/lists/* \ - && echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ - && locale-gen - -RUN adduser --disabled-password \ - --gecos "Default user" \ - --uid ${NB_UID} \ - --home ${HOME} \ - --force-badname \ - ${NB_USER} - -USER $NB_USER + && rm -rf /var/lib/apt/lists/* COPY requirements.txt /tmp/requirements.txt RUN pip install --no-cache-dir -r /tmp/requirements.txt -# nbgitpuller is installed in requirements.txt for demo purposes, and this -# enables it to function. -RUN jupyter serverextension enable --py nbgitpuller --sys-prefix +WORKDIR ${HOME} +USER ${NB_USER} +EXPOSE 8888 ENTRYPOINT ["tini", "--"] +CMD ["jupyter", "lab"] From 873f3ac21596ca3bb8ba0b746ed1b63733c3eb2e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 26 Aug 2022 10:14:48 +0200 Subject: [PATCH 513/898] Build psutil in a build stage --- images/singleuser-sample/Dockerfile | 36 +++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 00d59dbcb3..1ad4a69e8d 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,3 +1,31 @@ +# The build stage +# --------------- +FROM python:3.9-bullseye as build-stage + +# VULN_SCAN_TIME=2022-08-08_05:22:22 + +WORKDIR /build-stage + +# Build wheels for packages that require gcc or other build dependencies and +# lack wheels either for amd64 or aarch64. +# +# - https://pypi.org/project/psutil/#files, no wheels available as of 5.9.1 +# for aarch64. See https://github.com/giampaolo/psutil/pull/2070. +# +# If you find a dependency here no longer listed in requirements.txt, remove it +# from here as well. The more wheels we build here and copy and then install, +# where the copy and install are separate steps, the larger the final image +# becomes. +# +COPY requirements.txt requirements.txt +RUN pip install build \ + && pip wheel \ + $(cat requirements.txt | grep "psutil==") + + +# The final stage +# --------------- + FROM python:3.9-slim-bullseye # VULN_SCAN_TIME= @@ -19,13 +47,17 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ ca-certificates \ dnsutils \ - git \ iputils-ping \ tini \ + # requirement for nbgitpuller + git \ && rm -rf /var/lib/apt/lists/* +COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ COPY requirements.txt /tmp/requirements.txt -RUN pip install --no-cache-dir -r /tmp/requirements.txt +RUN pip install --no-cache-dir \ + /tmp/pre-built-wheels/*.whl \ + -r /tmp/requirements.txt WORKDIR ${HOME} USER ${NB_USER} From df48ddfc884a09d1d0b5539d89816753c8b712a5 Mon Sep 17 00:00:00 2001 From: Praveen Subramanian <46441970+praveen-ag@users.noreply.github.com> Date: Fri, 26 Aug 2022 17:02:05 +0530 Subject: [PATCH 514/898] Corrected indentation in the config.yml example at section `Modify your `config.yaml` file to specify the image`. --- docs/source/jupyterhub/customizing/user-environment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 14e2903474..524826d63a 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -44,9 +44,9 @@ image containing useful tools and libraries for data science, complete these ste # https://github.com/jupyter/docker-stacks/tree/HEAD/datascience-notebook/Dockerfile name: jupyter/datascience-notebook tag: latest - # `cmd: null` allows the custom CMD of the Jupyter docker-stacks to be used - # which performs further customization on startup. - cmd: null + # `cmd: null` allows the custom CMD of the Jupyter docker-stacks to be used + # which performs further customization on startup. + cmd: null ``` ```{note} From e38f180b0526c4b32e33f5903e70ece32e9e9bbc Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 30 Aug 2022 20:14:32 +0000 Subject: [PATCH 515/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 20 ++++++------- images/singleuser-sample/requirements.txt | 36 +++++++++++------------ 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index bcfda0d2d0..b88fc0bc1c 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -18,7 +18,7 @@ attrs==22.1.0 # via # aiohttp # jsonschema -bcrypt==3.2.2 +bcrypt==4.0.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator @@ -29,10 +29,8 @@ certifi==2022.6.15 certipy==0.1.3 # via jupyterhub cffi==1.15.1 - # via - # bcrypt - # cryptography -charset-normalizer==2.1.0 + # via cryptography +charset-normalizer==2.1.1 # via # aiohttp # requests @@ -46,7 +44,7 @@ frozenlist==1.3.1 # via # aiohttp # aiosignal -greenlet==1.1.2 +greenlet==1.1.3 # via sqlalchemy idna==3.3 # via @@ -58,7 +56,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.11.0 +jsonschema==4.14.0 # via # jupyter-telemetry # oauthenticator @@ -80,7 +78,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==4.1.0 +jupyterhub-kubespawner==4.2.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -90,11 +88,11 @@ jupyterhub-nativeauthenticator==1.0.5 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes-asyncio==24.2.1 +kubernetes-asyncio==24.2.2 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.2.1 +mako==1.2.2 # via alembic markupsafe==2.1.1 # via @@ -200,7 +198,7 @@ traitlets==5.3.0 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.11 +urllib3==1.26.12 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 6ab32eaacd..4b08606ab5 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -37,7 +37,7 @@ cffi==1.15.1 # via # argon2-cffi-bindings # cryptography -charset-normalizer==2.1.0 +charset-normalizer==2.1.1 # via requests cryptography==37.0.4 # via pyopenssl @@ -48,14 +48,12 @@ decorator==5.1.1 defusedxml==0.7.1 # via nbconvert entrypoints==0.4 - # via - # jupyter-client - # nbconvert -executing==0.10.0 + # via jupyter-client +executing==1.0.0 # via stack-data fastjsonschema==2.16.1 # via nbformat -greenlet==1.1.2 +greenlet==1.1.3 # via sqlalchemy idna==3.3 # via @@ -65,7 +63,8 @@ importlib-metadata==4.12.0 # via # jupyterhub # jupyterlab-server -ipykernel==6.15.1 + # nbconvert +ipykernel==6.15.2 # via # nbclassic # notebook @@ -88,14 +87,14 @@ jinja2==3.1.2 # nbclassic # nbconvert # notebook -json5==0.9.9 +json5==0.9.10 # via jupyterlab-server -jsonschema==4.11.0 +jsonschema==4.14.0 # via # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.3.4 +jupyter-client==7.3.5 # via # ipykernel # jupyter-server @@ -113,7 +112,6 @@ jupyter-core==4.11.1 # notebook jupyter-server==1.18.1 # via - # -r requirements.in # jupyterlab # jupyterlab-server # nbclassic @@ -130,13 +128,13 @@ jupyterlab==3.4.5 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.15.0 +jupyterlab-server==2.15.1 # via # jupyterlab # retrolab lxml==4.9.1 # via nbconvert -mako==1.2.1 +mako==1.2.2 # via alembic markupsafe==2.1.1 # via @@ -147,16 +145,16 @@ matplotlib-inline==0.1.6 # via # ipykernel # ipython -mistune==0.8.4 +mistune==2.0.4 # via nbconvert nbclassic==0.4.3 # via # -r requirements.in # jupyterlab # retrolab -nbclient==0.6.6 +nbclient==0.6.7 # via nbconvert -nbconvert==6.5.3 +nbconvert==7.0.0 # via # jupyter-server # nbclassic @@ -274,7 +272,7 @@ sqlalchemy==1.4.40 # via # alembic # jupyterhub -stack-data==0.4.0 +stack-data==0.5.0 # via ipython terminado==0.15.0 # via @@ -310,7 +308,7 @@ traitlets==5.3.0 # nbconvert # nbformat # notebook -urllib3==1.26.11 +urllib3==1.26.12 # via requests wcwidth==0.2.5 # via prompt-toolkit @@ -318,7 +316,7 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.3.3 +websocket-client==1.4.0 # via jupyter-server zipp==3.8.1 # via importlib-metadata From ae71b66c584374b75bdb482049e8eea7403e7fba Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 1 Sep 2022 13:14:17 +0200 Subject: [PATCH 516/898] Mount wheels from build stage instead of copying - avoids copying wheels, eliminating the need to track which need compilers on arm - mount pip cache as a cache dir, so cache can be used without polluting the image Requires buildkit for --mount results: smaller images, simpler Dockerfiles, faster rebuilds --- images/hub/Dockerfile | 37 ++++++++++++++++------------- images/singleuser-sample/Dockerfile | 35 ++++++++++++++------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index d5e0623481..488c07107b 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,3 +1,4 @@ +# syntax = docker/dockerfile:1.3 # The build stage # --------------- FROM python:3.9-bullseye as build-stage @@ -6,21 +7,16 @@ FROM python:3.9-bullseye as build-stage WORKDIR /build-stage -# Build wheels for packages that require gcc or other build dependencies and -# lack wheels either for amd64 or aarch64. -# -# - https://pypi.org/project/pycurl/#files, no wheels available as of 7.45.1. -# See https://github.com/pycurl/pycurl/issues/738. -# -# If you find a dependency here no longer listed in requirements.txt, remove it -# from here as well. The more wheels we build here and copy and then install, -# where the copy and install are separate steps, the larger the final image -# becomes. -# +# set env for pip cache, +# but use ARG to avoid persisting in image +ARG PIP_CACHE_DIR=/tmp/pip-cache + +# Build wheels +# These are mounted into the final image for installation COPY requirements.txt requirements.txt -RUN pip install build \ - && pip wheel \ - $(cat requirements.txt | grep "pycurl==") +RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + pip install build \ + && pip wheel -r requirements.txt # The final stage @@ -30,6 +26,7 @@ FROM python:3.9-slim-bullseye ARG NB_USER=jovyan ARG NB_UID=1000 ARG HOME=/home/jovyan + ENV DEBIAN_FRONTEND=noninteractive RUN adduser --disabled-password \ @@ -56,10 +53,16 @@ RUN apt-get update && \ tini \ && rm -rf /var/lib/apt/lists/* -COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ +# set env for pip cache, +# but use ARG to avoid persisting in image +ARG PIP_CACHE_DIR=/tmp/pip-cache + +# install wheels built in the build-stage COPY requirements.txt /tmp/requirements.txt -RUN pip install --no-cache-dir \ - /tmp/pre-built-wheels/*.whl \ +RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ + pip install \ + --find-links /tmp/wheels/ \ -r /tmp/requirements.txt WORKDIR /srv/jupyterhub diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 1ad4a69e8d..e398d524af 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,3 +1,4 @@ +# syntax = docker/dockerfile:1.3 # The build stage # --------------- FROM python:3.9-bullseye as build-stage @@ -6,21 +7,15 @@ FROM python:3.9-bullseye as build-stage WORKDIR /build-stage -# Build wheels for packages that require gcc or other build dependencies and -# lack wheels either for amd64 or aarch64. -# -# - https://pypi.org/project/psutil/#files, no wheels available as of 5.9.1 -# for aarch64. See https://github.com/giampaolo/psutil/pull/2070. -# -# If you find a dependency here no longer listed in requirements.txt, remove it -# from here as well. The more wheels we build here and copy and then install, -# where the copy and install are separate steps, the larger the final image -# becomes. -# +# set env for pip cache, +# but use ARG to avoid persisting in image +ARG PIP_CACHE_DIR=/tmp/pip-cache + +# These are mounted into the final image for installation COPY requirements.txt requirements.txt -RUN pip install build \ - && pip wheel \ - $(cat requirements.txt | grep "psutil==") +RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + pip install build \ + && pip wheel -r requirements.txt # The final stage @@ -53,10 +48,16 @@ RUN apt-get update \ git \ && rm -rf /var/lib/apt/lists/* -COPY --from=build-stage /build-stage/*.whl /tmp/pre-built-wheels/ +# set env for pip cache, +# but use ARG to avoid persisting in image +ARG PIP_CACHE_DIR=/tmp/pip-cache + +# install wheels built in the build-stage COPY requirements.txt /tmp/requirements.txt -RUN pip install --no-cache-dir \ - /tmp/pre-built-wheels/*.whl \ +RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ + pip install \ + --find-links /tmp/wheels/ \ -r /tmp/requirements.txt WORKDIR ${HOME} From 05319bbeafa88b1c2f3f9ad03e787a2dfc7e1954 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 1 Sep 2022 13:54:58 +0200 Subject: [PATCH 517/898] build with buildkit on CI --- .circleci/config.yml | 1 + .github/workflows/test-chart.yaml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index bf4b79de35..87016b1591 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,6 +52,7 @@ jobs: - run: command: | + export DOCKER_BUILDKIT=1 chartpress name: Run chartpress diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 4b790819a3..445d872821 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -247,6 +247,8 @@ jobs: run: | pip3 install -r dev-requirements.txt chartpress + env: + DOCKER_BUILDKIT: "1" # Generate values.schema.json from schema.yaml - name: Generate values.schema.json from schema.yaml From 2dd123f4589ed2fd39c0dad7220e0ad70e640b23 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 1 Sep 2022 15:43:27 +0200 Subject: [PATCH 518/898] Clarify pip cache ARG choice comment Co-authored-by: Erik Sundell --- images/hub/Dockerfile | 4 ++-- images/singleuser-sample/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 488c07107b..7860122e69 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -53,8 +53,8 @@ RUN apt-get update && \ tini \ && rm -rf /var/lib/apt/lists/* -# set env for pip cache, -# but use ARG to avoid persisting in image +# set pip's cache directory using this environment variable, and use +# ARG instead of ENV to ensure its only set when the image is built ARG PIP_CACHE_DIR=/tmp/pip-cache # install wheels built in the build-stage diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index e398d524af..c4b4197b69 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -7,8 +7,8 @@ FROM python:3.9-bullseye as build-stage WORKDIR /build-stage -# set env for pip cache, -# but use ARG to avoid persisting in image +# set pip's cache directory using this environment variable, and use +# ARG instead of ENV to ensure its only set when the image is built ARG PIP_CACHE_DIR=/tmp/pip-cache # These are mounted into the final image for installation From 3e235cf4eec7da6b34e2e1cbbb7efc050f217b68 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 1 Sep 2022 15:44:32 +0200 Subject: [PATCH 519/898] use equals sign to specify find-links Co-authored-by: Erik Sundell --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index c4b4197b69..544ac6b77c 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -57,7 +57,7 @@ COPY requirements.txt /tmp/requirements.txt RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ pip install \ - --find-links /tmp/wheels/ \ + --find-links=/tmp/wheels/ \ -r /tmp/requirements.txt WORKDIR ${HOME} From 7e51044a778ffa0a259c18d0246d343054135878 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 1 Sep 2022 15:56:11 +0200 Subject: [PATCH 520/898] Clarify pip cache ARG choice comment (again) --- images/hub/Dockerfile | 4 ++-- images/singleuser-sample/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 7860122e69..991aa134dd 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -7,8 +7,8 @@ FROM python:3.9-bullseye as build-stage WORKDIR /build-stage -# set env for pip cache, -# but use ARG to avoid persisting in image +# set pip's cache directory using this environment variable, and use +# ARG instead of ENV to ensure its only set when the image is built ARG PIP_CACHE_DIR=/tmp/pip-cache # Build wheels diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 544ac6b77c..5d1258462b 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -48,8 +48,8 @@ RUN apt-get update \ git \ && rm -rf /var/lib/apt/lists/* -# set env for pip cache, -# but use ARG to avoid persisting in image +# set pip's cache directory using this environment variable, and use +# ARG instead of ENV to ensure its only set when the image is built ARG PIP_CACHE_DIR=/tmp/pip-cache # install wheels built in the build-stage From c5adeae406b2d4441ca1ec71f9a2c8a7e67769fc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 1 Sep 2022 15:57:03 +0200 Subject: [PATCH 521/898] use equals sign to specify find-links (again) --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 991aa134dd..f3781a09a0 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -62,7 +62,7 @@ COPY requirements.txt /tmp/requirements.txt RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ pip install \ - --find-links /tmp/wheels/ \ + --find-links=/tmp/wheels/ \ -r /tmp/requirements.txt WORKDIR /srv/jupyterhub From 867f4331d97f0f89ff9433edeb18c8afefd6d83b Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 3 Sep 2022 05:29:10 +0000 Subject: [PATCH 522/898] Update library/traefik version from v2.8.3 to v2.8.4 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 3ed3ce563e..5ef0032fd4 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.3" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 778b030fd452453bd18b722788000255f735ce3b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 6 Sep 2022 15:40:51 +0200 Subject: [PATCH 523/898] ci: disable dependabot bumping singleuser-sample deps We have a dedicated github workflow to smoothly update requirements that runs on a schedule and can be run manually if needed as well. When it is run, a PR will be created updating the requirements.txt file based on the requirements.in file using `pip-compile`. --- .github/dependabot.yaml | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 334c2ae4f2..007d3d634e 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -4,27 +4,9 @@ # - Status and logs from dependabot are provided at # https://github.com/jupyterhub/zero-to-jupyterhub-k8s/network/updates. # - YAML anchors are not supported here or in GitHub Workflows. -# - versioning-strategy: lockfile-only must not be used if you only have a plain -# requirements.txt like we do in images/singleuser-sample. # version: 2 updates: - # Maintain Python dependencies for the jupyterhub/k8s-singleuser-sample image - - package-ecosystem: pip - directory: "/images/singleuser-sample" - schedule: - interval: daily - time: "05:00" - timezone: "Etc/UTC" - # ignore is applied as we need to bump this version in multiple locations, - # and not just in the singleuser-sample image, for which we have a dedicated - # job in the watch-dependencies.yaml workflow file. - # - ignore: - - dependency-name: jupyterhub - labels: - - dependencies - # Maintain dependencies in our GitHub Workflows - package-ecosystem: github-actions directory: "/" # This should be / rather than .github/workflows @@ -32,5 +14,3 @@ updates: interval: weekly time: "05:00" timezone: "Etc/UTC" - labels: - - dependencies From 70afc746994921f4f02144fe40f350ce5367deae Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 7 Sep 2022 10:01:37 +0200 Subject: [PATCH 524/898] Update chartpress to 2.0 - require chartpress 2 in dev-requirements - set baseVersion in chartpress.yaml - note baseVersion bump in release process --- RELEASE.md | 2 ++ chartpress.yaml | 1 + dev-requirements.txt | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 10df04e3bb..4130d9f6b6 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -93,6 +93,8 @@ Also the images we build are based on some image specified in the `FROM` stateme git push --follow-tags main ``` + - [ ] Update baseVersion in chartpress to the next release (e.g. `2.1.0-0.dev` after 2.0.0) + - [ ] Create a GitHub release. Visit the [release page](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases) and create a new release referencing the recent tag. Add a brief text like the one below. diff --git a/chartpress.yaml b/chartpress.yaml index 5594084cba..62c0d565b1 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,6 +13,7 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- + baseVersion: "2.0.0-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/dev-requirements.txt b/dev-requirements.txt index a999cc71cd..efeb4f8eb7 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,7 +5,7 @@ # # ref: https://github.com/jupyterhub/chartpress # -chartpress>=1.1.0 +chartpress==2.* # pytest run tests that require requests and pyyaml pytest>=3.7.1 From e1aaaa595dd656e871d309493795a97645418934 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 7 Sep 2022 14:02:11 +0200 Subject: [PATCH 525/898] docs: backfill early changelog entries based on github releases --- docs/source/changelog.md | 44 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 0237c8c74d..f207325814 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -3520,20 +3520,56 @@ the greatest pace bowler of all time and a founder of the fine art of ### 0.3.1 - 2017-05-19 -KubeSpawner updates. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.3.1) +KubeSpawner updates. + +- KubeSpawner has gained several new features, thanks + to the work of Daniel Rodriguez and ktongsc! Specifically, + we have support for init containers, node selectors, + pod lifecycle hooks, etc. These can be used with the + extraConfig override for now +- Add easy ability to specify pod lifecycle hooks via the + helm chart! ### 0.3 - 2017-05-15 -Deployer UX fixes. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.3) +Deployer UX fixes. + +- No need to restart hub manually after some changes - it is + automatically restarted now. You can disable an automatic + restart of hub after an upgrade with the following: + + 1. Finding out the current helm release's revision + 2. Adding '--set revisionOverride=' to your + upgrade command. + + Only do this if you know exactly what you are doing :) + +- Base images for everything upgraded to ubuntu 17.04. We can + define the support lifecycle for the helm chart in the future, + and decide on the base images at that point. +- Add a timestamp to the job name for the pre-puller job. This + prevents having to manually delete it when an install fails and + has to be tried again. Because the Release Revision hadn't changed + when the upgrade fails, trying again will cause it to fail with a + 'job already exists' error. Adding the Timestamp to job name should + hopefully fix that ## 0.2 ### 0.2 - 2017-05-01 -Minor cleanups and features. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.2) +Minor cleanups and features. + +- Get rid of cull pod, move it inside the hub pod as a + managed service +- Set a default 1G memory guarantee for user pods +- Allow setting a static global password for Dummy Authenticator +- Allow setting extra static environment variables for user pods + from the helm config +- Upgrade kubespawner version (no major functional changes) ## 0.1 ### 0.1 - 2017-04-10 -Initial Public Release. [Release note](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases/tag/v0.1) +Initial Public Release. From 643f77c23d9064a552212977c2fda2b18e716ffc Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 7 Sep 2022 15:35:53 +0200 Subject: [PATCH 526/898] use tbump to bump versions ensures baseVersion is always in sync with tag --- RELEASE.md | 12 +++++++----- dev-requirements.txt | 3 +++ tbump.toml | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 tbump.toml diff --git a/RELEASE.md b/RELEASE.md index 4130d9f6b6..f77aa7479d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -65,8 +65,7 @@ Also the images we build are based on some image specified in the `FROM` stateme ```bash git checkout main git reset --hard /main - git tag -a x.y.z-beta.1 -m x.y.z-beta.1 - git push --follow-tags main + tbump x.y.z-beta.1 ``` - Announce the x.y.z-beta.1 release @@ -89,11 +88,14 @@ Also the images we build are based on some image specified in the `FROM` stateme ```bash git checkout main git reset --hard /main - git tag -a x.y.z -m x.y.z HEAD - git push --follow-tags main + tbump x.y.z ``` - - [ ] Update baseVersion in chartpress to the next release (e.g. `2.1.0-0.dev` after 2.0.0) + - [ ] Set the next prerelease version (don't create a tag). + + ```bash + tbump --no-tag x.y+1.z-0.dev + ``` - [ ] Create a GitHub release. Visit the [release page](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases) and create a new release referencing the recent tag. Add a brief text like the one below. diff --git a/dev-requirements.txt b/dev-requirements.txt index efeb4f8eb7..aa83753bb8 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -17,3 +17,6 @@ click # used to formatting and linting pre-commit>=2.0.0 + +# used for tagging releases +tbump diff --git a/tbump.toml b/tbump.toml new file mode 100644 index 0000000000..a1e1bfeace --- /dev/null +++ b/tbump.toml @@ -0,0 +1,35 @@ +# tbump config sets baseVersion in chartpress.yaml +[version] +current = "2.0.0-0.dev" + +# match our prerelease prefixes +# -alpha.1 +# -beta.2 +# -0.dev + +regex = ''' + (?P\d+) + \. + (?P\d+) + \. + (?P\d+) + (\- + (?P + ( + (alpha|beta|rc)\.\d+| + 0\.dev + ) + ) + )? + ''' + +[git] +message_template = "Bump to {new_version}" +tag_template = "{new_version}" + +# For each file to patch, add a [[file]] config +# section containing the path of the file, relative to the +# tbump.toml location. +[[file]] +src = "chartpress.yaml" +search = 'baseVersion: "{current_version}"' From e3eb6ce06dcb30857118c4e12ceffb67a0ebe07e Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 7 Sep 2022 15:37:09 +0200 Subject: [PATCH 527/898] Bump to 2.0.0-beta.1 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 62c0d565b1..20b6b3a5f0 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,7 +13,7 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- - baseVersion: "2.0.0-0.dev" + baseVersion: "2.0.0-beta.1" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index a1e1bfeace..25dc6aae2b 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,6 +1,6 @@ # tbump config sets baseVersion in chartpress.yaml [version] -current = "2.0.0-0.dev" +current = "2.0.0-beta.1" # match our prerelease prefixes # -alpha.1 From 2ee8cad4729c15cdc44d9e6c2a8da725f3957a57 Mon Sep 17 00:00:00 2001 From: Jakob Kolb Date: Thu, 8 Sep 2022 00:34:02 +0200 Subject: [PATCH 528/898] Fix docs for github org membership based auth To read members of an organization, the scope has to be read:org, not read:user. Debugging this cost me half a day and I hope fixing this can save others the time. --- docs/source/administrator/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 21694d3b4e..7f28764aab 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -175,7 +175,7 @@ hub: allowed_organizations: - my-github-organization scope: - - read:user + - read:org ``` If you would like to restrict access to a specific team within a GitHub organization, use From f7b736290f41f6fed25f44b0d39e80f5da3e85a9 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 8 Sep 2022 09:31:28 +0200 Subject: [PATCH 529/898] Apply suggestions from code review Co-authored-by: Erik Sundell --- RELEASE.md | 2 +- dev-requirements.txt | 2 +- tbump.toml | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index f77aa7479d..b1c89bf40f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -94,7 +94,7 @@ Also the images we build are based on some image specified in the `FROM` stateme - [ ] Set the next prerelease version (don't create a tag). ```bash - tbump --no-tag x.y+1.z-0.dev + tbump --no-tag x.y.z+1-0.dev ``` - [ ] Create a GitHub release. diff --git a/dev-requirements.txt b/dev-requirements.txt index aa83753bb8..adf4067751 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,7 +5,7 @@ # # ref: https://github.com/jupyterhub/chartpress # -chartpress==2.* +chartpress>=2.1,<3 # pytest run tests that require requests and pyyaml pytest>=3.7.1 diff --git a/tbump.toml b/tbump.toml index 25dc6aae2b..bf197e4ac6 100644 --- a/tbump.toml +++ b/tbump.toml @@ -1,4 +1,9 @@ -# tbump config sets baseVersion in chartpress.yaml +# tbump is a tool to update version fields that we use +# to update baseVersion in chartpress.yaml as documented +# in RELEASE.md +# +# Config reference: https://github.com/your-tools/tbump#readme +# [version] current = "2.0.0-beta.1" From 7d8e8d67096c24ea28d08b4a1a4195dba30f7594 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Sep 2022 07:06:55 +0000 Subject: [PATCH 530/898] Update jupyterhub from 3.0.0b1 to 3.0.0 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 12 ++++++------ images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 23 ++++++++++------------- jupyterhub/Chart.yaml | 2 +- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 73987da962..a64e3648bb 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.0.0b1 +jupyterhub==3.0.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b88fc0bc1c..5e1e0a22c3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -34,7 +34,7 @@ charset-normalizer==2.1.1 # via # aiohttp # requests -cryptography==37.0.4 +cryptography==38.0.1 # via pyopenssl escapism==1.0.1 # via @@ -56,13 +56,13 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.14.0 +jsonschema==4.15.0 # via # jupyter-telemetry # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.0.0b1 +jupyterhub==3.0.0 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -106,7 +106,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==15.0.1 +oauthenticator==15.1.0 # via -r requirements.in oauthlib==3.2.0 # via @@ -124,7 +124,7 @@ prometheus-client==0.14.1 # via jupyterhub psycopg2-binary==2.9.3 # via -r requirements.in -py-spy==0.3.12 +py-spy==0.3.14 # via -r requirements.in pyasn1==0.4.8 # via ldap3 @@ -177,7 +177,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.40 +sqlalchemy==1.4.41 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index a5c149b7de..3a8249ea66 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.0.0b1 +jupyterhub==3.0.0 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 4b08606ab5..ce0f5e3f7a 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -39,7 +39,7 @@ cffi==1.15.1 # cryptography charset-normalizer==2.1.1 # via requests -cryptography==37.0.4 +cryptography==38.0.1 # via pyopenssl debugpy==1.6.3 # via ipykernel @@ -68,7 +68,7 @@ ipykernel==6.15.2 # via # nbclassic # notebook -ipython==8.4.0 +ipython==8.5.0 # via # ipykernel # jupyterlab @@ -89,7 +89,7 @@ jinja2==3.1.2 # notebook json5==0.9.10 # via jupyterlab-server -jsonschema==4.14.0 +jsonschema==4.15.0 # via # jupyter-telemetry # jupyterlab-server @@ -120,9 +120,9 @@ jupyter-server==1.18.1 # retrolab jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.0.0b1 +jupyterhub==3.0.0 # via -r requirements.in -jupyterlab==3.4.5 +jupyterlab==3.4.6 # via # -r requirements.in # retrolab @@ -207,9 +207,9 @@ prometheus-client==0.14.1 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.30 +prompt-toolkit==3.0.31 # via ipython -psutil==5.9.1 +psutil==5.9.2 # via ipykernel ptyprocess==0.7.0 # via @@ -264,11 +264,11 @@ six==1.16.0 # asttokens # bleach # python-dateutil -sniffio==1.2.0 +sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==1.4.40 +sqlalchemy==1.4.41 # via # alembic # jupyterhub @@ -316,10 +316,7 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.4.0 +websocket-client==1.4.1 # via jupyter-server zipp==3.8.1 # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -# setuptools diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index d0271e7931..ef529f5250 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "3.0.0b1" +appVersion: "3.0.0" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 800abe89ac5625ac6f374da07488c0f58e707be3 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Sep 2022 07:25:37 +0000 Subject: [PATCH 531/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5e1e0a22c3..3c15356ee0 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==4.0.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.6.15 +certifi==2022.6.15.1 # via # kubernetes-asyncio # requests diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index ce0f5e3f7a..b985675a32 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -29,7 +29,7 @@ beautifulsoup4==4.11.1 # via nbconvert bleach==5.0.1 # via nbconvert -certifi==2022.6.15 +certifi==2022.6.15.1 # via requests certipy==0.1.3 # via jupyterhub From f1b0da1b7d65c26a8505b8d90d1664de0ab07c41 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Sep 2022 08:29:58 +0000 Subject: [PATCH 532/898] Update jupyterhub/configurable-http-proxy version from 4.5.2 to 4.5.3 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5ef0032fd4..6b863b9fe4 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -198,7 +198,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.2" # https://github.com/jupyterhub/configurable-http-proxy/releases + tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From e1ff6dbcc2b48e1296f73e04c82b522f595b4a8b Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Sep 2022 11:34:48 +0000 Subject: [PATCH 533/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3c15356ee0..7a78951d77 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -56,7 +56,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.15.0 +jsonschema==4.16.0 # via # jupyter-telemetry # oauthenticator @@ -84,7 +84,7 @@ jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in jupyterhub-ltiauthenticator==1.2.0 # via -r requirements.in -jupyterhub-nativeauthenticator==1.0.5 +jupyterhub-nativeauthenticator==1.1.0 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index b985675a32..c9184da238 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -89,7 +89,7 @@ jinja2==3.1.2 # notebook json5==0.9.10 # via jupyterlab-server -jsonschema==4.15.0 +jsonschema==4.16.0 # via # jupyter-telemetry # jupyterlab-server From 688fb6d203b1ba1bf63168a8754de47c5c60fd57 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 29 Aug 2022 12:13:55 +0200 Subject: [PATCH 534/898] Update changelog for 2.0.0 --- docs/source/changelog.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 0237c8c74d..6a5d8f534c 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,12 +14,7 @@ this list should be updated. ## 2.0 -### 2.0.0 (currently beta.1) - -WARNING: This is a beta release of the JupyterHub Helm chart. - -We do not expect to make any significant changes between this and the final 2.0.0 release unless major bugs are found. -Ensure you backup your system and test any upgrades if you plan to use this in production. +### 2.0.0 - 2022-09-09 #### Highlights @@ -53,7 +48,7 @@ Please read through all breaking changes, then follow the [upgrading guide](admi These breaking changes have been made relative to the 1.\* series of Z2JH releases: - Security: breaking change to `*.networkPolicy.egress` -- JupyterHub upgraded to 2.x along with related hub components +- JupyterHub upgraded from 1.x to 3.x along with related hub components - JupyterLab and Jupyter Server is now the default singleuser application - Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) - Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) @@ -65,20 +60,22 @@ These breaking changes have been made relative to the 1.\* series of Z2JH releas For information on how to update your configuration see the [](administrator/upgrading/upgrade-1-to-2) guide. +(notable-dependencies-200)= + #### Notable dependencies updated | Dependency | Version in 1.2.0 | Version in 2.0.0 | Changelog link | Note | | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | -| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 1.4.2 | 3.0.0b1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | -| [kubespawner](https://github.com/jupyterhub/kubespawner) | 1.1.0 | 4.1.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 14.2.0 | 15.0.1 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 1.4.2 | 3.0.0 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | +| [kubespawner](https://github.com/jupyterhub/kubespawner) | 1.1.0 | 4.2.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 14.2.0 | 15.1.0 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.0.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | -| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 0.0.7 | 1.0.5 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 0.0.7 | 1.1.0 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | -| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.0 | 4.5.1 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | -| [traefik](https://github.com/traefik/traefik) | v2.4.11 | v2.8.3 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | -| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.19.13 | v1.23.9 | - | Run in the `user-scheduler` pod(s) | +| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.0 | 4.5.3 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | +| [traefik](https://github.com/traefik/traefik) | v2.4.11 | v2.8.4 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.19.13 | v1.23.10 | - | Run in the `user-scheduler` pod(s) | For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt) file and use its git history to see what changes between tagged versions. @@ -115,6 +112,8 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Maintenance and upkeep improvements +- hub image: remove workaround for ruamel.yaml.clib on aarch64 [#2846](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2846) ([@consideRatio](https://github.com/consideRatio)) +- Make the singleuser-sample image use python:3.9-slim-bullseye as a base image to retain arm64 support [#2845](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2845) ([@minrk](https://github.com/minrk)) - Restore jupyterhub-singleuser as the default command [#2820](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2820) ([@minrk](https://github.com/minrk)) - Adjust kerning on large JupyterHub in NOTES.txt [#2787](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2787) ([@manics](https://github.com/manics)) - hub image: remove wheel building aarch64 workaround for pycryptodomex [#2766](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2766) ([@consideRatio](https://github.com/consideRatio)) From cf3f00a7ee4beaea12df86693721d93f3f6b13a9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 9 Sep 2022 13:51:00 +0200 Subject: [PATCH 535/898] Bump to 2.0.0 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 20b6b3a5f0..5d198624c0 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,7 +13,7 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- - baseVersion: "2.0.0-beta.1" + baseVersion: "2.0.0" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index bf197e4ac6..bd5bd9f533 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "2.0.0-beta.1" +current = "2.0.0" # match our prerelease prefixes # -alpha.1 From 31f29885a86cb5b91daeb9ab315f737c3ff07aa0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 9 Sep 2022 13:51:26 +0200 Subject: [PATCH 536/898] Bump to 2.0.1-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 5d198624c0..6fc7cdd8d5 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,7 +13,7 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- - baseVersion: "2.0.0" + baseVersion: "2.0.1-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index bd5bd9f533..574314a4ba 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "2.0.0" +current = "2.0.1-0.dev" # match our prerelease prefixes # -alpha.1 From 9a40975fde07a4bccd910fe32a74e790f194c248 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 14 Sep 2022 06:02:23 +0000 Subject: [PATCH 537/898] Update library/traefik version from v2.8.4 to v2.8.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 6b863b9fe4..aa4f8cd118 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.5" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 71d5745796c72889911d977436edfaebeae29a48 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 15 Sep 2022 05:57:56 +0000 Subject: [PATCH 538/898] Update kube-scheduler version from v1.23.10 to v1.23.11 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index aa4f8cd118..d7c922190e 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.11" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From f475e7a40c3224ee61481e788867ccd0c65f9a44 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 17 Sep 2022 06:01:10 +0000 Subject: [PATCH 539/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 10 +++++----- images/singleuser-sample/requirements.txt | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7a78951d77..7ffe949fc3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==4.0.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.6.15.1 +certifi==2022.9.14 # via # kubernetes-asyncio # requests @@ -46,7 +46,7 @@ frozenlist==1.3.1 # aiosignal greenlet==1.1.3 # via sqlalchemy -idna==3.3 +idna==3.4 # via # requests # yarl @@ -108,7 +108,7 @@ nullauthenticator==1.0.0 # via -r requirements.in oauthenticator==15.1.0 # via -r requirements.in -oauthlib==3.2.0 +oauthlib==3.2.1 # via # jupyterhub # jupyterhub-ltiauthenticator @@ -182,7 +182,7 @@ sqlalchemy==1.4.41 # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.3 +sqlalchemy-cockroachdb==1.4.4 # via -r requirements.in statsd==3.3.0 # via -r requirements.in @@ -193,7 +193,7 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.3.0 +traitlets==5.4.0 # via # jupyter-telemetry # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index c9184da238..69bdb99217 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -29,7 +29,7 @@ beautifulsoup4==4.11.1 # via nbconvert bleach==5.0.1 # via nbconvert -certifi==2022.6.15.1 +certifi==2022.9.14 # via requests certipy==0.1.3 # via jupyterhub @@ -55,7 +55,7 @@ fastjsonschema==2.16.1 # via nbformat greenlet==1.1.3 # via sqlalchemy -idna==3.3 +idna==3.4 # via # anyio # requests @@ -64,7 +64,7 @@ importlib-metadata==4.12.0 # jupyterhub # jupyterlab-server # nbconvert -ipykernel==6.15.2 +ipykernel==6.15.3 # via # nbclassic # notebook @@ -122,7 +122,7 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==3.0.0 # via -r requirements.in -jupyterlab==3.4.6 +jupyterlab==3.4.7 # via # -r requirements.in # retrolab @@ -152,14 +152,14 @@ nbclassic==0.4.3 # -r requirements.in # jupyterlab # retrolab -nbclient==0.6.7 +nbclient==0.6.8 # via nbconvert nbconvert==7.0.0 # via # jupyter-server # nbclassic # notebook -nbformat==5.4.0 +nbformat==5.5.0 # via # jupyter-server # nbclassic @@ -181,7 +181,7 @@ notebook==6.4.12 # nbgitpuller notebook-shim==0.1.0 # via nbclassic -oauthlib==3.2.0 +oauthlib==3.2.1 # via jupyterhub packaging==21.3 # via @@ -237,7 +237,7 @@ python-json-logger==2.0.4 # via jupyter-telemetry pytz==2022.2.1 # via babel -pyzmq==23.2.1 +pyzmq==24.0.0 # via # ipykernel # jupyter-client @@ -281,6 +281,8 @@ terminado==0.15.0 # notebook tinycss2==1.1.1 # via nbconvert +tomli==2.0.1 + # via jupyterlab tornado==6.2 # via # ipykernel @@ -293,7 +295,7 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.3.0 +traitlets==5.4.0 # via # ipykernel # ipython From dd1c418a77dd08964a720b7953797d2a36aa1e13 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 15:50:44 +0100 Subject: [PATCH 540/898] docs: auth defaults to dummy --- docs/source/administrator/authentication.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 7f28764aab..5326733174 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -13,8 +13,10 @@ Before configuring this, you should have [setup HTTPS](https). ### Authenticator classes -JupyterHub by default ships with only one source of authentication: -PAM, the underlying unix authentication of the host system. +Z2JH defaults to a [DummyAuthenticator](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.DummyAuthenticator) +that allows anyone to login with any username and password. +This should only be used for testing purposes. + To use other sources of authentication, choose _one_ [_authenticator class_](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html) to use. Several such classes are already available in the hub image through [installed From 9776f8ab4a4c64b5cd333bea2585bb6663261920 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 15:56:22 +0100 Subject: [PATCH 541/898] docs: remove /auth from keycloak URLs Current Keycloak version is 19. Versions 17+ do not have `/auth` in their URLs https://www.keycloak.org/migration/migrating-to-quarkus --- docs/source/administrator/authentication.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 7f28764aab..de918c4047 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -376,7 +376,7 @@ hub: [KeyCloak](https://www.keycloak.org) is an open source based provider of identity management that you can host yourself. Below is an example on how you can configure the GenericOAuthenticator class to authenticate against a KeyCloak -server. +server (version 17 or later). To configure an OpenID Connect client, see [KeyCloak's own documentation](https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_clients). @@ -388,9 +388,9 @@ hub: client_id: your-client-id client_secret: your-client-secret oauth_callback_url: https://your-jupyterhub-domain/hub/oauth_callback - authorize_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/auth - token_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/token - userdata_url: https://${host}/auth/realms/${realm}/protocol/openid-connect/userinfo + authorize_url: https://${host}/realms/${realm}/protocol/openid-connect/auth + token_url: https://${host}/realms/${realm}/protocol/openid-connect/token + userdata_url: https://${host}/realms/${realm}/protocol/openid-connect/userinfo login_service: keycloak username_key: preferred_username userdata_params: From 283f150e3c33c033b8da3192278f3d8f79d1fcc2 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 16:12:47 +0100 Subject: [PATCH 542/898] docs: fix git sha lookup for dev builds the linkcheck is currently broken --- docs/source/conf.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7cc5bd0ba5..3da048b539 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -37,13 +37,14 @@ def setup(app): def _get_git_ref_from_chartpress_based_version(version): """ Get a git ref from a chartpress set version of format like - 1.2.3-beta.1.n123.h1234567, 1.2.3-n123.h1234567, or 1.2.3. + - 2.0.1-0.dev.git.5810.hf475e7a4 return git hash + - 2.0.0-beta.1 return tag + - 2.0.0 return tag """ - tag_hash_split = re.split(r"[\.|-]n\d\d\d\.h", version) - if len(tag_hash_split) == 2: - return tag_hash_split[1] - else: - return tag_hash_split[0] + m = re.match(r"\d+\.\d+\.\d+(-.+\.h([0-9a-f]+))$", version) + if m: + return m.group(2) + return version # FIXME: Stop relying on chartpress to modify Chart.yaml (and values.yaml) by From e6b9ecec280a43f6f1008e3e2e9286d4582ce86b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 16:37:43 +0100 Subject: [PATCH 543/898] values.yaml: fix link to configurable-http-proxy releaes --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index d7c922190e..7c15d72ff0 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -198,7 +198,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases + tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From c562083535704ceb8d526a049ac83ec4f21745a3 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 20:39:13 +0100 Subject: [PATCH 544/898] ci: Auto-create GitHub release when repo is tagged --- .github/workflows/release-tag.yml | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/release-tag.yml diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml new file mode 100644 index 0000000000..586b95a1f8 --- /dev/null +++ b/.github/workflows/release-tag.yml @@ -0,0 +1,32 @@ +# Create a GitHub release whenever a tag is pushed. +# Tags that aren't in the form N.N.N are considered pre-releases. + +name: release-tag +on: + push: + tags: + - "**" + +jobs: + create-release: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + # https://github.com/actions/github-script + # https://octokit.github.io/rest.js/v18#repos-create-release + - uses: actions/github-script@v6 + with: + script: | + if (!context.ref.startsWith('refs/tags/')) { + core.setFailed(`${context.ref} is not in the form refs/tags/$tag`) + } + const tag = context.ref.slice(10) + const prerelease = !tag.match(/\d+\.\d+\.\d+$/) + github.rest.repos.createRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + tag_name: tag, + prerelease: prerelease, + body: 'Please see the [changelog](https://zero-to-jupyterhub.readthedocs.io/en/latest/changelog.html) for details.' + }); From f38ea29f0f212e016b52037351dd55bad636258c Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 18 Sep 2022 20:53:22 +0100 Subject: [PATCH 545/898] Remove github release manual step from RELEASE.md --- RELEASE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index b1c89bf40f..d4a4b40ad6 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -67,6 +67,7 @@ Also the images we build are based on some image specified in the `FROM` stateme git reset --hard /main tbump x.y.z-beta.1 ``` + This will automatically create a [GitHub prerelease](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases). - Announce the x.y.z-beta.1 release - [ ] Write a discourse post @@ -91,14 +92,13 @@ Also the images we build are based on some image specified in the `FROM` stateme tbump x.y.z ``` + This will automatically create a [GitHub release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases). + - [ ] Set the next prerelease version (don't create a tag). ```bash tbump --no-tag x.y.z+1-0.dev ``` - - [ ] Create a GitHub release. - Visit the [release page](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/releases) and create a new release referencing the recent tag. Add a brief text like the one below. - - Communicate - [ ] Update the beta release's discourse post. From e844b8469cade1c63d6893cd7f3aee644a13d997 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 19 Sep 2022 08:24:19 +0200 Subject: [PATCH 546/898] ci: enable buildkit for vuln scan workflow as needed for --mount --- .github/workflows/vuln-scan.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 7f7a452875..c38d385bac 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -104,6 +104,8 @@ jobs: - name: Rebuild image id: rebuild if: steps.scan_1.outcome == 'failure' + env: + DOCKER_BUILDKIT: "1" run: | docker build -t rebuilt-image images/${{ matrix.image_ref }} From 688926117f701e86fe3968e8325e84cfa2275257 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 19 Sep 2022 08:34:30 +0200 Subject: [PATCH 547/898] secret sync image: use python 3.9 --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 8832e35484..77332a2685 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-alpine +FROM python:3.9-alpine # VULN_SCAN_TIME=2022-08-15_05:35:56 From c88116c774b04c73d1c2b48a3ee16a232a112ef0 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 22 Sep 2022 05:59:04 +0000 Subject: [PATCH 548/898] Update kube-scheduler version from v1.23.11 to v1.23.12 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7c15d72ff0..493d016ad8 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.11" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.12" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From c8d491dc4e48bc21e31a12b534d3184cc4aaf256 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 23 Sep 2022 15:18:12 +0000 Subject: [PATCH 549/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- images/singleuser-sample/requirements.txt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7ffe949fc3..5c906664dc 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.8.1 +aiohttp==3.8.3 # via kubernetes-asyncio aiosignal==1.2.0 # via aiohttp @@ -92,7 +92,7 @@ kubernetes-asyncio==24.2.2 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.2.2 +mako==1.2.3 # via alembic markupsafe==2.1.1 # via @@ -132,7 +132,7 @@ pycparser==2.21 # via cffi pycurl==7.45.1 # via -r requirements.in -pyjwt==2.4.0 +pyjwt==2.5.0 # via # -r requirements.in # mwoauth diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 69bdb99217..59ee58c3f7 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -51,7 +51,7 @@ entrypoints==0.4 # via jupyter-client executing==1.0.0 # via stack-data -fastjsonschema==2.16.1 +fastjsonschema==2.16.2 # via nbformat greenlet==1.1.3 # via sqlalchemy @@ -134,7 +134,7 @@ jupyterlab-server==2.15.1 # retrolab lxml==4.9.1 # via nbconvert -mako==1.2.2 +mako==1.2.3 # via alembic markupsafe==2.1.1 # via @@ -159,7 +159,7 @@ nbconvert==7.0.0 # jupyter-server # nbclassic # notebook -nbformat==5.5.0 +nbformat==5.6.0 # via # jupyter-server # nbclassic @@ -237,7 +237,7 @@ python-json-logger==2.0.4 # via jupyter-telemetry pytz==2022.2.1 # via babel -pyzmq==24.0.0 +pyzmq==24.0.1 # via # ipykernel # jupyter-client From db887f4917a222d7c8420a730014b3f6712735ba Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 24 Sep 2022 05:40:06 +0000 Subject: [PATCH 550/898] Update library/traefik version from v2.8.5 to v2.8.7 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 493d016ad8..7e0bd0d50d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.5" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.7" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 3ade882db62cdcb0341eb6f7c7ede41eff8236f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 05:02:52 +0000 Subject: [PATCH 551/898] build(deps): bump peter-evans/create-pull-request from 4.1.1 to 4.1.2 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.1 to 4.1.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/18f90432bedd2afd6a825469ffd38aa24712a91d...171dd555b9ab6b18fa02519fdfacbb8bf671e1b4) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index c38d385bac..c09c3dcd38 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d + uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index ff575e0acd..6a4d1cb31a 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d + uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -181,7 +181,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d + uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -214,7 +214,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d + uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 448525a72e95dfb029da01042137d2c710e42741 Mon Sep 17 00:00:00 2001 From: Uula Ranta Date: Mon, 26 Sep 2022 11:55:30 +0300 Subject: [PATCH 552/898] Remove unreleased reverted change from changelog --- docs/source/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 45258d8f2c..cb690459a7 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -50,7 +50,6 @@ These breaking changes have been made relative to the 1.\* series of Z2JH releas - Security: breaking change to `*.networkPolicy.egress` - JupyterHub upgraded from 1.x to 3.x along with related hub components - JupyterLab and Jupyter Server is now the default singleuser application -- Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) - Configuration in `jupyterhub_config.d` has a higher priority than `hub.config` [#2457](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2457) - User scheduler plugin configuration has changed to match `kubescheduler.config.k8s.io/v1beta3` [#2590](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2590) - Kubernetes version 1.20+ is required [#2635](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2635) @@ -115,6 +114,7 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp - hub image: remove workaround for ruamel.yaml.clib on aarch64 [#2846](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2846) ([@consideRatio](https://github.com/consideRatio)) - Make the singleuser-sample image use python:3.9-slim-bullseye as a base image to retain arm64 support [#2845](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2845) ([@minrk](https://github.com/minrk)) - Restore jupyterhub-singleuser as the default command [#2820](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2820) ([@minrk](https://github.com/minrk)) + - Reverted unreleased breaking change: Default to using the container image's command instead of `jupyterhub-singleuser` [#2449](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2449) - Adjust kerning on large JupyterHub in NOTES.txt [#2787](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2787) ([@manics](https://github.com/manics)) - hub image: remove wheel building aarch64 workaround for pycryptodomex [#2766](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2766) ([@consideRatio](https://github.com/consideRatio)) - hub image: downgrade to ltiauthenticator 1.2.0 [#2741](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2741) ([@consideRatio](https://github.com/consideRatio)) From c244e20e12fbb199ac9e81ceacc5c615035c8937 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 26 Sep 2022 09:58:07 +0000 Subject: [PATCH 553/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.txt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5c906664dc..c2a8d0d619 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==4.0.0 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.9.14 +certifi==2022.9.24 # via # kubernetes-asyncio # requests @@ -138,7 +138,7 @@ pyjwt==2.5.0 # mwoauth pymysql==1.0.2 # via -r requirements.in -pyopenssl==22.0.0 +pyopenssl==22.1.0 # via certipy pyparsing==3.0.9 # via packaging diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 59ee58c3f7..674f414e8b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -29,7 +29,7 @@ beautifulsoup4==4.11.1 # via nbconvert bleach==5.0.1 # via nbconvert -certifi==2022.9.14 +certifi==2022.9.24 # via requests certipy==0.1.3 # via jupyterhub @@ -49,7 +49,7 @@ defusedxml==0.7.1 # via nbconvert entrypoints==0.4 # via jupyter-client -executing==1.0.0 +executing==1.1.0 # via stack-data fastjsonschema==2.16.2 # via nbformat @@ -223,7 +223,7 @@ pygments==2.13.0 # via # ipython # nbconvert -pyopenssl==22.0.0 +pyopenssl==22.1.0 # via certipy pyparsing==3.0.9 # via packaging @@ -272,7 +272,7 @@ sqlalchemy==1.4.41 # via # alembic # jupyterhub -stack-data==0.5.0 +stack-data==0.5.1 # via ipython terminado==0.15.0 # via From 9ed9fa65a9c39737244c481de0c54a69b2ee767a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 1 Oct 2022 05:56:20 +0000 Subject: [PATCH 554/898] Update library/traefik version from v2.8.7 to v2.8.8 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7e0bd0d50d..c24585604f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.7" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.8.8" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From a0a3c25ad780c1a33da11d2785db260f1e5d6fe3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 05:02:25 +0000 Subject: [PATCH 555/898] build(deps): bump peter-evans/create-pull-request from 4.1.2 to 4.1.3 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/171dd555b9ab6b18fa02519fdfacbb8bf671e1b4...671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index c09c3dcd38..0b2048df79 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 6a4d1cb31a..8a4463f6c3 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -181,7 +181,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -214,7 +214,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@171dd555b9ab6b18fa02519fdfacbb8bf671e1b4 + uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 71ad22872532bb1c6697e1f38453e4a8dde73e8c Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 3 Oct 2022 11:49:15 +0000 Subject: [PATCH 556/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index c2a8d0d619..8b32a4c44c 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -50,7 +50,7 @@ idna==3.4 # via # requests # yarl -importlib-metadata==4.12.0 +importlib-metadata==5.0.0 # via jupyterhub jinja2==3.1.2 # via diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 674f414e8b..163725e46c 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -59,12 +59,12 @@ idna==3.4 # via # anyio # requests -importlib-metadata==4.12.0 +importlib-metadata==5.0.0 # via # jupyterhub # jupyterlab-server # nbconvert -ipykernel==6.15.3 +ipykernel==6.16.0 # via # nbclassic # notebook @@ -110,7 +110,7 @@ jupyter-core==4.11.1 # nbconvert # nbformat # notebook -jupyter-server==1.18.1 +jupyter-server==1.19.1 # via # jupyterlab # jupyterlab-server @@ -128,7 +128,7 @@ jupyterlab==3.4.7 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.15.1 +jupyterlab-server==2.15.2 # via # jupyterlab # retrolab @@ -147,7 +147,7 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.4 # via nbconvert -nbclassic==0.4.3 +nbclassic==0.4.4 # via # -r requirements.in # jupyterlab @@ -159,7 +159,7 @@ nbconvert==7.0.0 # jupyter-server # nbclassic # notebook -nbformat==5.6.0 +nbformat==5.6.1 # via # jupyter-server # nbclassic @@ -168,7 +168,7 @@ nbformat==5.6.0 # notebook nbgitpuller==1.1.0 # via -r requirements.in -nest-asyncio==1.5.5 +nest-asyncio==1.5.6 # via # ipykernel # jupyter-client @@ -235,7 +235,7 @@ python-dateutil==2.8.2 # jupyterhub python-json-logger==2.0.4 # via jupyter-telemetry -pytz==2022.2.1 +pytz==2022.4 # via babel pyzmq==24.0.1 # via @@ -274,7 +274,7 @@ sqlalchemy==1.4.41 # jupyterhub stack-data==0.5.1 # via ipython -terminado==0.15.0 +terminado==0.16.0 # via # jupyter-server # nbclassic From c3ea6952f2a3169b55670ad85814d90e204c83aa Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 4 Oct 2022 05:40:29 +0000 Subject: [PATCH 557/898] Update library/traefik version from v2.8.8 to v2.9.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c24585604f..eabf80c67f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.8.8" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.1" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From b9fd8fd8ae9ae6bc4b6388df6ed64cfcd45b6ded Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 10 Oct 2022 06:01:29 +0000 Subject: [PATCH 558/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 77332a2685..9df0f545fb 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.9-alpine -# VULN_SCAN_TIME=2022-08-15_05:35:56 +# VULN_SCAN_TIME=2022-10-10_06:01:28 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 2c72d76e226b2fe533c408e64693bf5010e48040 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Oct 2022 00:29:13 +0000 Subject: [PATCH 559/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.3 → v3.1.0](https://github.com/asottile/pyupgrade/compare/v2.37.3...v3.1.0) - [github.com/psf/black: 22.6.0 → 22.10.0](https://github.com/psf/black/compare/22.6.0...22.10.0) - [github.com/pre-commit/mirrors-prettier: v2.7.1 → v3.0.0-alpha.1](https://github.com/pre-commit/mirrors-prettier/compare/v2.7.1...v3.0.0-alpha.1) - [github.com/jupyterhub/chartpress: 1.3.0 → 2.1.0](https://github.com/jupyterhub/chartpress/compare/1.3.0...2.1.0) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9688201e0c..33a96b194e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v2.37.3 + rev: v3.1.0 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 22.6.0 + rev: 22.10.0 hooks: - id: black args: @@ -53,12 +53,12 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 + rev: v3.0.0-alpha.1 hooks: - id: prettier # Reset Chart.yaml version and values.yaml image tags - repo: https://github.com/jupyterhub/chartpress - rev: 1.3.0 + rev: 2.1.0 hooks: - id: chartpress From e4dbf6c8b48d12454a9abd663e6589661abe8832 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 13 Oct 2022 05:56:33 +0000 Subject: [PATCH 560/898] Update kube-scheduler version from v1.23.12 to v1.23.13 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index eabf80c67f..147c3f6db7 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.12" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.13" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 3434973f66fe63996538eae08f42361c3e6bb7d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 05:02:20 +0000 Subject: [PATCH 561/898] build(deps): bump docker/setup-buildx-action from 2.0.0 to 2.1.0 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2.0.0 to 2.1.0. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/dc7b9719a96d48369863986a06765841d7ea23f6...95cb08cb2672c73d4ffd2f422e6d11953d2a9c70) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index bc43904e0e..e901f3ca14 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -77,7 +77,7 @@ jobs: uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@dc7b9719a96d48369863986a06765841d7ea23f6 # dependabot updates to latest release + uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index a7f835da4c..bed85d2dbc 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -48,7 +48,7 @@ jobs: uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@dc7b9719a96d48369863986a06765841d7ea23f6 # dependabot updates to latest release + uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release - name: Build a multiple architecture Docker image run: >- From 81f1fce814873903a69e9fbaaf9238aa701fee25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 05:07:35 +0000 Subject: [PATCH 562/898] build(deps): bump docker/setup-qemu-action from 2.0.0 to 2.1.0 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2.0.0 to 2.1.0. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/8b122486cedac8393e77aa9734c3528886e4a1a8...e81a89b1732b9c48d79cd809d8d81d79c4647a18) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e901f3ca14..d449b7487a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -74,7 +74,7 @@ jobs: print(f"::set-output name=publishing::{publishing}") - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release + uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index bed85d2dbc..3a6af34c58 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -45,7 +45,7 @@ jobs: run: pip install chartpress - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@8b122486cedac8393e77aa9734c3528886e4a1a8 # dependabot updates to latest release + uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # dependabot updates to latest release - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release From 06c4600f7734b5fb6b7c53432a95c79a08cee627 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 23:44:45 +0000 Subject: [PATCH 563/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.1 → v3.0.0-alpha.2](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.1...v3.0.0-alpha.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 33a96b194e..3e90afe327 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,7 +53,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.1 + rev: v3.0.0-alpha.2 hooks: - id: prettier From 52044de7d600cf57c90f2005b6b0b948a2e41b3d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 22 Oct 2022 12:58:31 +0200 Subject: [PATCH 564/898] docs: update readthedocs config to ubuntu build --- .readthedocs.yaml | 17 +++++++++++++++++ .readthedocs.yml | 25 ------------------------- 2 files changed, 17 insertions(+), 25 deletions(-) create mode 100644 .readthedocs.yaml delete mode 100644 .readthedocs.yml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..66af176525 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,17 @@ +# Configuration on how ReadTheDocs (RTD) builds our documentation +# ref: https://readthedocs.org/projects/zero-to-jupyterhub/ +# ref: https://docs.readthedocs.io/en/stable/config-file/v2.html +# +version: 2 + +sphinx: + configuration: docs/source/conf.py + +build: + os: ubuntu-22.04 + tools: + python: "3.10" + +python: + install: + - requirements: docs/requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 12eca699cc..0000000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,25 +0,0 @@ -# Configuration on how ReadTheDocs (RTD) builds our documentation -# ref: https://readthedocs.org/projects/zero-to-jupyterhub/ -# ref: https://docs.readthedocs.io/en/stable/config-file/v2.html - -# Required (RTD configuration version) -version: 2 - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: docs/source/conf.py - -# Optionally build your docs in additional formats such as PDF and ePub -formats: [] - -# Optionally set the version of Python and requirements required to build your docs -python: - version: 3.7 - install: - # WARNING: This requirements file will be installed without the pip - # --upgrade flag in an existing environment. This means that if a - # package is specified without a lower boundary, we may end up - # accepting the existing version. - # - # ref: https://github.com/readthedocs/readthedocs.org/blob/0e3df509e7810e46603be47d268273c596e68455/readthedocs/doc_builder/python_environments.py#L335-L344 - - requirements: docs/requirements.txt From 7c24a47c41201c36bd3ba97c19dfbe2b10d99d7d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 22 Oct 2022 12:59:30 +0200 Subject: [PATCH 565/898] docs: update conf.py like repo2docker and binderhub was --- docs/requirements.txt | 9 -- docs/source/conf.py | 272 +++++++++++++++++++----------------------- 2 files changed, 123 insertions(+), 158 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 82adf09e9b..fa53c111a6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,12 +1,3 @@ -# The ordering of these requirements can matter. It matters if we install them -# in an environment where a dependency to them (such as sphinx) is -# pre-installed, such as it can be on RTD. For example, if a top-ordered package -# declares it needs sphinx>=1 and then a later package declares it needs -# sphinx<4 - the latter may be ignored. -# -# As long as we have myst-parser ordered before other sphinx depending packages, -# we should be good. -# chartpress myst-parser pydata-sphinx-theme diff --git a/docs/source/conf.py b/docs/source/conf.py index 3da048b539..edceb80261 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -2,20 +2,6 @@ # # Configuration reference: https://www.sphinx-doc.org/en/master/usage/configuration.html # - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project specific imports ------------------------------------------------ - import datetime import os import re @@ -23,17 +9,39 @@ import yaml -# -- Sphinx setup function --------------------------------------------------- -# ref: https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events +# -- Project information ----------------------------------------------------- +# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +# +project = "Zero to JupyterHub with Kubernetes" +copyright = f"{datetime.date.today().year}, Project Jupyter Contributors" +author = "Project Jupyter Contributors" -def setup(app): - app.add_css_file("custom.css") +# -- General Sphinx configuration --------------------------------------------------- +# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration +# +extensions = [ + "myst_parser", + "sphinx_copybutton", + "sphinx.ext.mathjax", + "sphinxext.opengraph", + "sphinxext.rediraffe", +] +root_doc = "index" +source_suffix = [".md"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -# -- Referenceable variables -------------------------------------------------- +# -- General MyST configuration ----------------------------------------------------- +# ref: https://myst-parser.readthedocs.io/en/latest/configuration.html +# +myst_enable_extensions = [ + "substitution", +] +# -- Referenceable variables -------------------------------------------------- +# def _get_git_ref_from_chartpress_based_version(version): """ Get a git ref from a chartpress set version of format like @@ -72,114 +80,8 @@ def _get_git_ref_from_chartpress_based_version(version): } -# -- General MyST configuration ----------------------------------------------------- - -# myst_enable_extensions ref: https://myst-parser.readthedocs.io/en/latest/using/syntax-optional.html -myst_enable_extensions = [ - "substitution", -] - - -# -- Project information ----------------------------------------------------- -# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -project = "Zero to JupyterHub with Kubernetes" -copyright = f"{datetime.date.today().year}, Project Jupyter Contributors" -author = "Project Jupyter Contributors" - - -# -- General Sphinx configuration --------------------------------------------------- -# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -# Set the default role so we can use `foo` instead of ``foo`` -default_role = "literal" - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - "sphinx.ext.mathjax", - "sphinx_copybutton", - "myst_parser", - "sphinxext.rediraffe", - "sphinxext.opengraph", -] - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [] - -# The root toctree document. -root_doc = master_doc = "index" - -# The suffix(es) of source filenames. -source_suffix = [".md", ".rst"] - -# Rediraffe redirects to ensure proper redirection -rediraffe_redirects = { - "customizing/user-management": "jupyterhub/customizing/user-management", - "customizing/user-storage": "jupyterhub/customizing/user-storage", - "customizing/user-resources": "jupyterhub/customizing/user-resources", - "customizing/user-environment": "jupyterhub/customizing/user-environment", - "customizing/extending-jupyterhub": "jupyterhub/customizing/extending-jupyterhub", - "reference/glossary": "resources/glossary", - "reference/tools": "resources/tools", - "reference/reference-docs": "resources/reference-docs", - "reference/reference": "resources/reference", - "community/additional-resources": "resources/community", - "community/users-list": "resources/community", - "community/tips": "resources/community", - "setup-jupyterhub/turn-off": "jupyterhub/uninstall", - "setup-jupyterhub/setup-jupyterhub": "jupyterhub/installation", - "setup-jupyterhub/setup-helm": "kubernetes/setup-helm", - "ovh/step-zero-ovh": "kubernetes/ovh/step-zero-ovh", - "digital-ocean/step-zero-digital-ocean": "kubernetes/digital-ocean/step-zero-digital-ocean", - "ibm/step-zero-ibm": "kubernetes/ibm/step-zero-ibm", - "redhat/step-zero-openshift": "kubernetes/redhat/step-zero-openshift", - "amazon/step-zero-aws-eks": "kubernetes/amazon/step-zero-aws-eks", - "amazon/step-zero-aws": "kubernetes/amazon/step-zero-aws", - "microsoft/step-zero-azure-autoscale": "kubernetes/microsoft/step-zero-azure", - "microsoft/step-zero-azure": "kubernetes/microsoft/step-zero-azure", - "google/step-zero-gcp": "kubernetes/google/step-zero-gcp", - "create-k8s-cluster": "kubernetes/setup-kubernetes", - "turn-off": "jupyterhub/uninstall", - "setup-jupyterhub": "jupyterhub/index", - "setup-helm": "kubernetes/setup-helm", - "index-setup-jupyterhub": "jupyterhub/index", - "tools": "reference/tools", - "reference-docs": "reference/reference-docs", - "index-reference": "resources/reference", - "glossary": "reference/glossary", - "user-storage": "customizing/user-storage", - "user-resources": "customizing/user-resources", - "user-management": "customizing/user-management", - "user-environment": "customizing/user-environment", - "index-customization-guide": "jupyterhub/customization", - "extending-jupyterhub": "customizing/extending-jupyterhub", - "users-list": "community/users-list", - "tips": "community/tips", - "index-community-resources": "resources/community", - "additional-resources": "resources/community", - "upgrading": "administrator/upgrading/index", - "troubleshooting": "administrator/troubleshooting", - "security": "administrator/security", - "optimization": "administrator/optimization", - "index-administrator-guide": "administrator/index", - "debug": "administrator/debug", - "cost": "administrator/cost", - "authentication": "administrator/authentication", - "architecture": "administrator/architecture", - "advanced": "administrator/advanced", -} - -# opengraph configuration -# ogp_site_url/prefix is set automatically by RTD -ogp_image = "_static/logo.png" -ogp_use_first_image = True - # -- Generate the Helm chart configuration reference from a schema file ------ - +# # header with open("resources/reference.txt") as f: header_md = f.readlines() @@ -224,8 +126,32 @@ def parse_schema(d, md=[], depth=0, pre=""): f.write("\n".join(reference_md)) +# -- Options for HTML output ------------------------------------------------- +# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output +# +html_logo = "_static/images/logo/logo.png" +html_favicon = "_static/images/logo/favicon.ico" +html_static_path = ["_static"] +html_css_files = ["custom.css"] + +# pydata_sphinx_theme reference: https://pydata-sphinx-theme.readthedocs.io/en/latest/ +html_theme = "pydata_sphinx_theme" +html_theme_options = { + "github_url": "https://github.com/jupyterhub/zero-to-jupyterhub-k8s/", + "use_edit_page_button": True, + "twitter_url": "https://twitter.com/mybinderteam", +} +html_context = { + "github_user": "jupyterhub", + "github_repo": "zero-to-jupyterhub-k8s", + "github_version": "main", + "doc_path": "docs/source", +} + + # -- Options for linkcheck builder ------------------------------------------- # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-the-linkcheck-builder +# linkcheck_ignore = [ r"(.*)github\.com(.*)#", # javascript based anchors r"(.*)/#%21(.*)/(.*)", # /#!forum/jupyter - encoded anchor edge case @@ -246,29 +172,77 @@ def parse_schema(d, md=[], depth=0, pre=""): ] -# -- Options for HTML output ------------------------------------------------- -# ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. +# -- Options for the opengraph extension ------------------------------------- +# ref: https://github.com/wpilibsuite/sphinxext-opengraph#options # +# This extension help others provide better thumbnails and link descriptions +# when they link to this documentation from other websites, such as +# https://discourse.jupyter.org. +# +# ogp_site_url is set automatically by RTD +ogp_image = "_static/logo.png" +ogp_use_first_image = True -html_theme = "pydata_sphinx_theme" -html_theme_options = { - "github_url": "https://github.com/jupyterhub/zero-to-jupyterhub-k8s/", - "use_edit_page_button": True, -} -html_context = { - "github_user": "jupyterhub", - "github_repo": "zero-to-jupyterhub-k8s", - "github_version": "main", - "doc_path": "docs/source", -} - -html_favicon = "_static/images/logo/favicon.ico" -html_logo = "_static/images/logo/logo.png" -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ["_static"] +# -- Options for the rediraffe extension ------------------------------------- +# ref: https://github.com/wpilibsuite/sphinxext-rediraffe#readme +# +# This extensions help us relocated content without breaking links. If a +# document is moved internally, we should configure a redirect like below. +# +rediraffe_branch = "main" +rediraffe_redirects = { + "customizing/user-management": "jupyterhub/customizing/user-management", + "customizing/user-storage": "jupyterhub/customizing/user-storage", + "customizing/user-resources": "jupyterhub/customizing/user-resources", + "customizing/user-environment": "jupyterhub/customizing/user-environment", + "customizing/extending-jupyterhub": "jupyterhub/customizing/extending-jupyterhub", + "reference/glossary": "resources/glossary", + "reference/tools": "resources/tools", + "reference/reference-docs": "resources/reference-docs", + "reference/reference": "resources/reference", + "community/additional-resources": "resources/community", + "community/users-list": "resources/community", + "community/tips": "resources/community", + "setup-jupyterhub/turn-off": "jupyterhub/uninstall", + "setup-jupyterhub/setup-jupyterhub": "jupyterhub/installation", + "setup-jupyterhub/setup-helm": "kubernetes/setup-helm", + "ovh/step-zero-ovh": "kubernetes/ovh/step-zero-ovh", + "digital-ocean/step-zero-digital-ocean": "kubernetes/digital-ocean/step-zero-digital-ocean", + "ibm/step-zero-ibm": "kubernetes/ibm/step-zero-ibm", + "redhat/step-zero-openshift": "kubernetes/redhat/step-zero-openshift", + "amazon/step-zero-aws-eks": "kubernetes/amazon/step-zero-aws-eks", + "amazon/step-zero-aws": "kubernetes/amazon/step-zero-aws", + "microsoft/step-zero-azure-autoscale": "kubernetes/microsoft/step-zero-azure", + "microsoft/step-zero-azure": "kubernetes/microsoft/step-zero-azure", + "google/step-zero-gcp": "kubernetes/google/step-zero-gcp", + "create-k8s-cluster": "kubernetes/setup-kubernetes", + "turn-off": "jupyterhub/uninstall", + "setup-jupyterhub": "jupyterhub/index", + "setup-helm": "kubernetes/setup-helm", + "index-setup-jupyterhub": "jupyterhub/index", + "tools": "reference/tools", + "reference-docs": "reference/reference-docs", + "index-reference": "resources/reference", + "glossary": "reference/glossary", + "user-storage": "customizing/user-storage", + "user-resources": "customizing/user-resources", + "user-management": "customizing/user-management", + "user-environment": "customizing/user-environment", + "index-customization-guide": "jupyterhub/customization", + "extending-jupyterhub": "customizing/extending-jupyterhub", + "users-list": "community/users-list", + "tips": "community/tips", + "index-community-resources": "resources/community", + "additional-resources": "resources/community", + "upgrading": "administrator/upgrading/index", + "troubleshooting": "administrator/troubleshooting", + "security": "administrator/security", + "optimization": "administrator/optimization", + "index-administrator-guide": "administrator/index", + "debug": "administrator/debug", + "cost": "administrator/cost", + "authentication": "administrator/authentication", + "architecture": "administrator/architecture", + "advanced": "administrator/advanced", +} From 2f143a863a7bdbba77daf98d3df759bee7fc98ba Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 24 Oct 2022 01:58:36 +0200 Subject: [PATCH 566/898] Switch from deprecated k8s.gcr.io to registry.k8s.io --- .github/workflows/watch-dependencies.yaml | 4 ++-- jupyterhub/values.yaml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 8a4463f6c3..099b41d95a 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -65,14 +65,14 @@ jobs: # values.yaml about bumping its version. # - name: kube-scheduler - registry: k8s.gcr.io + registry: registry.k8s.io repository: kube-scheduler values_path: scheduling.userScheduler.image.tag version_startswith: "v1.23" version_patch_regexp_group_suffix: "" - name: pause - registry: k8s.gcr.io + registry: registry.k8s.io repository: pause values_path: scheduling.userPlaceholder.image.tag version_startswith: "" diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index eabf80c67f..c973eb66bd 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -499,7 +499,7 @@ scheduling: # - If "successfully acquired lease" can be seen in the logs, it # is a good sign kube-scheduler is ready to schedule pods. # - name: k8s.gcr.io/kube-scheduler + name: registry.k8s.io/kube-scheduler # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. The minor version is pinned in the # workflow, and should be updated there if a minor version bump is done @@ -531,7 +531,7 @@ scheduling: userPlaceholder: enabled: true image: - name: k8s.gcr.io/pause + name: registry.k8s.io/pause # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # @@ -617,7 +617,7 @@ prePuller: runAsGroup: 65534 # nobody group allowPrivilegeEscalation: false image: - name: k8s.gcr.io/pause + name: registry.k8s.io/pause # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # From 17b8da1adf76c8d4a15ebba150d8f85b6227c085 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 24 Oct 2022 00:22:42 +0000 Subject: [PATCH 567/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 20 ++++----- images/singleuser-sample/requirements.txt | 51 +++++++++++------------ 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 8b32a4c44c..2c56fbf3e3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -18,7 +18,7 @@ attrs==22.1.0 # via # aiohttp # jsonschema -bcrypt==4.0.0 +bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator @@ -44,7 +44,7 @@ frozenlist==1.3.1 # via # aiohttp # aiosignal -greenlet==1.1.3 +greenlet==1.1.3.post0 # via sqlalchemy idna==3.4 # via @@ -108,7 +108,7 @@ nullauthenticator==1.0.0 # via -r requirements.in oauthenticator==15.1.0 # via -r requirements.in -oauthlib==3.2.1 +oauthlib==3.2.2 # via # jupyterhub # jupyterhub-ltiauthenticator @@ -120,9 +120,9 @@ packaging==21.3 # via jupyterhub pamela==1.0.0 # via jupyterhub -prometheus-client==0.14.1 +prometheus-client==0.15.0 # via jupyterhub -psycopg2-binary==2.9.3 +psycopg2-binary==2.9.4 # via -r requirements.in py-spy==0.3.14 # via -r requirements.in @@ -132,7 +132,7 @@ pycparser==2.21 # via cffi pycurl==7.45.1 # via -r requirements.in -pyjwt==2.5.0 +pyjwt==2.6.0 # via # -r requirements.in # mwoauth @@ -169,7 +169,7 @@ ruamel-yaml==0.17.21 # via # jupyter-telemetry # oauthenticator -ruamel-yaml-clib==0.2.6 +ruamel-yaml-clib==0.2.7 # via ruamel-yaml six==1.16.0 # via @@ -177,7 +177,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.41 +sqlalchemy==1.4.42 # via # alembic # jupyterhub @@ -193,7 +193,7 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.4.0 +traitlets==5.5.0 # via # jupyter-telemetry # jupyterhub @@ -205,7 +205,7 @@ urllib3==1.26.12 # requests yarl==1.8.1 # via aiohttp -zipp==3.8.1 +zipp==3.9.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 163725e46c..3feded4b1d 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -6,7 +6,7 @@ # alembic==1.8.1 # via jupyterhub -anyio==3.6.1 +anyio==3.6.2 # via jupyter-server argon2-cffi==21.3.0 # via @@ -49,11 +49,11 @@ defusedxml==0.7.1 # via nbconvert entrypoints==0.4 # via jupyter-client -executing==1.1.0 +executing==1.1.1 # via stack-data fastjsonschema==2.16.2 # via nbformat -greenlet==1.1.3 +greenlet==1.1.3.post0 # via sqlalchemy idna==3.4 # via @@ -64,7 +64,7 @@ importlib-metadata==5.0.0 # jupyterhub # jupyterlab-server # nbconvert -ipykernel==6.16.0 +ipykernel==6.16.1 # via # nbclassic # notebook @@ -94,14 +94,14 @@ jsonschema==4.16.0 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.3.5 +jupyter-client==7.4.3 # via # ipykernel # jupyter-server # nbclassic # nbclient # notebook -jupyter-core==4.11.1 +jupyter-core==4.11.2 # via # jupyter-client # jupyter-server @@ -110,7 +110,7 @@ jupyter-core==4.11.1 # nbconvert # nbformat # notebook -jupyter-server==1.19.1 +jupyter-server==1.21.0 # via # jupyterlab # jupyterlab-server @@ -122,18 +122,16 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==3.0.0 # via -r requirements.in -jupyterlab==3.4.7 +jupyterlab==3.4.8 # via # -r requirements.in # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.15.2 +jupyterlab-server==2.16.1 # via # jupyterlab # retrolab -lxml==4.9.1 - # via nbconvert mako==1.2.3 # via alembic markupsafe==2.1.1 @@ -147,19 +145,20 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.4 # via nbconvert -nbclassic==0.4.4 +nbclassic==0.4.5 # via # -r requirements.in # jupyterlab + # notebook # retrolab -nbclient==0.6.8 +nbclient==0.7.0 # via nbconvert -nbconvert==7.0.0 +nbconvert==7.2.2 # via # jupyter-server # nbclassic # notebook -nbformat==5.6.1 +nbformat==5.7.0 # via # jupyter-server # nbclassic @@ -175,13 +174,13 @@ nest-asyncio==1.5.6 # nbclassic # nbclient # notebook -notebook==6.4.12 +notebook==6.5.1 # via # jupyterlab # nbgitpuller -notebook-shim==0.1.0 +notebook-shim==0.2.0 # via nbclassic -oauthlib==3.2.1 +oauthlib==3.2.2 # via jupyterhub packaging==21.3 # via @@ -201,7 +200,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -prometheus-client==0.14.1 +prometheus-client==0.15.0 # via # jupyter-server # jupyterhub @@ -209,7 +208,7 @@ prometheus-client==0.14.1 # notebook prompt-toolkit==3.0.31 # via ipython -psutil==5.9.2 +psutil==5.9.3 # via ipykernel ptyprocess==0.7.0 # via @@ -235,7 +234,7 @@ python-dateutil==2.8.2 # jupyterhub python-json-logger==2.0.4 # via jupyter-telemetry -pytz==2022.4 +pytz==2022.5 # via babel pyzmq==24.0.1 # via @@ -252,7 +251,7 @@ retrolab==0.3.21 # via -r requirements.in ruamel-yaml==0.17.21 # via jupyter-telemetry -ruamel-yaml-clib==0.2.6 +ruamel-yaml-clib==0.2.7 # via ruamel-yaml send2trash==1.8.0 # via @@ -268,7 +267,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==1.4.41 +sqlalchemy==1.4.42 # via # alembic # jupyterhub @@ -279,7 +278,7 @@ terminado==0.16.0 # jupyter-server # nbclassic # notebook -tinycss2==1.1.1 +tinycss2==1.2.1 # via nbconvert tomli==2.0.1 # via jupyterlab @@ -295,7 +294,7 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.4.0 +traitlets==5.5.0 # via # ipykernel # ipython @@ -320,5 +319,5 @@ webencodings==0.5.1 # tinycss2 websocket-client==1.4.1 # via jupyter-server -zipp==3.8.1 +zipp==3.9.0 # via importlib-metadata From cff8f2e480e34da55907181120c0046580ee7c20 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 24 Oct 2022 10:48:27 +0200 Subject: [PATCH 568/898] ci: bump docker action versions to v2 from v2.x.y --- .github/workflows/publish.yml | 4 ++-- .github/workflows/test-docker-build.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d449b7487a..9df4a9e4f8 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -74,10 +74,10 @@ jobs: print(f"::set-output name=publishing::{publishing}") - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # dependabot updates to latest release + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release + uses: docker/setup-buildx-action@v2 - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 3a6af34c58..e99ec84d91 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -45,10 +45,10 @@ jobs: run: pip install chartpress - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # dependabot updates to latest release + uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@95cb08cb2672c73d4ffd2f422e6d11953d2a9c70 # dependabot updates to latest release + uses: docker/setup-buildx-action@v2 - name: Build a multiple architecture Docker image run: >- From c6d5fc6c7aea08031d8d70a83021ca1e9c05f6c6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Oct 2022 23:09:53 +0000 Subject: [PATCH 569/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.2 → v3.0.0-alpha.3](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.2...v3.0.0-alpha.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e90afe327..f83a4b841b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,7 +53,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.2 + rev: v3.0.0-alpha.3 hooks: - id: prettier From b0afac900596c9a573843ba07031a4da5a12b8cd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 25 Oct 2022 17:09:30 +0200 Subject: [PATCH 570/898] Apply suggestions from code review Co-authored-by: Simon Li --- docs/source/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index edceb80261..1e4caa690d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -139,7 +139,6 @@ def parse_schema(d, md=[], depth=0, pre=""): html_theme_options = { "github_url": "https://github.com/jupyterhub/zero-to-jupyterhub-k8s/", "use_edit_page_button": True, - "twitter_url": "https://twitter.com/mybinderteam", } html_context = { "github_user": "jupyterhub", From 7de20b77470f4c1b4bad23598434f751264e79e8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 27 Oct 2022 14:54:29 +0200 Subject: [PATCH 571/898] Add a k8s-hub-slim image --- .github/workflows/test-chart.yaml | 4 ++- chartpress.yaml | 6 +++++ images/hub/Dockerfile | 43 +++++++++++++++++++++---------- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 445d872821..13d02bbc0c 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -131,8 +131,10 @@ jobs: include: - k3s-channel: latest test: install - - k3s-channel: stable + - k3s-channel: stable # also test hub-slim test: install + local-chart-extra-args: >- + --set hub.image.name=jupyterhub/k8s-hub-slim - k3s-channel: v1.21 # also test prePuller.hook test: install local-chart-extra-args: >- diff --git a/chartpress.yaml b/chartpress.yaml index 6fc7cdd8d5..c141c82b95 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -23,6 +23,12 @@ charts: # Authenticator are running. hub: valuesPath: hub.image + # hub-slim, an alternative hub image that doesn't include some + # basic utilities for k8s admins + hub-slim: + contextPath: images/hub + extraBuildCommandOptions: + - --target=slim-stage # secret-sync, a sidecar container running in the autohttps pod to next to # Traefik meant to sync a TLS certificate with a k8s Secret. diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index f3781a09a0..428ac74928 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -19,9 +19,12 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ && pip wheel -r requirements.txt -# The final stage -# --------------- -FROM python:3.9-slim-bullseye +# The final stage - slim version +# ------------------------------ +# This stage is built and published as jupyterhub/k8s-hub-slim. It is meant to +# provide no apt packages besides whats essential. +# +FROM python:3.9-slim-bullseye as slim-stage ARG NB_USER=jovyan ARG NB_UID=1000 @@ -36,16 +39,9 @@ RUN adduser --disabled-password \ --force-badname \ ${NB_USER} -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get install -y --no-install-recommends \ - # misc network utilities - curl \ - dnsutils \ - git \ - # misc other utilities - less \ - vim \ +RUN apt-get update \ + && apt-get upgrade -y \ + && apt-get install -y --no-install-recommends \ # requirement for pycurl libcurl4 \ # requirement for using a local sqlite database @@ -72,3 +68,24 @@ USER ${NB_USER} EXPOSE 8081 ENTRYPOINT ["tini", "--"] CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] + + +# The final stage - default version +# --------------------------------- +# We add a few non-critical apt packages on top of the slim version to provide +# some debugging utility for k8s admins. +# +FROM slim-stage + +USER root +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + # misc network utilities + curl \ + dnsutils \ + git \ + # misc other utilities + less \ + vim \ + && rm -rf /var/lib/apt/lists/* +USER ${NB_USER} From e8579b3e7b1a4c616cd9b1097482bc3b58b67aa9 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 28 Oct 2022 05:24:49 +0000 Subject: [PATCH 572/898] Update library/traefik version from v2.9.1 to v2.9.4 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 93df559540..453f00a4d8 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.1" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.4" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 44ec9f11a863981e103496782bce1db974fa3790 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 30 Oct 2022 19:24:40 +0100 Subject: [PATCH 573/898] hub/singleuser-sample: refactoring and comments --- images/hub/Dockerfile | 50 ++++++++++++++--------------- images/singleuser-sample/Dockerfile | 44 ++++++++++++------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 428ac74928..6c00a051b3 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,38 +1,41 @@ # syntax = docker/dockerfile:1.3 -# The build stage -# --------------- -FROM python:3.9-bullseye as build-stage - # VULN_SCAN_TIME=2022-08-08_05:22:22 -WORKDIR /build-stage -# set pip's cache directory using this environment variable, and use -# ARG instead of ENV to ensure its only set when the image is built -ARG PIP_CACHE_DIR=/tmp/pip-cache +# The build stage +# --------------- +# This stage is building Python wheels for use in later stages by using a base +# image that has more pre-requisites to do so, such as a C++ compiler. +# +FROM python:3.9-bullseye as build-stage # Build wheels -# These are mounted into the final image for installation +# +# We set pip's cache directory and expose it across build stages via an +# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). +# COPY requirements.txt requirements.txt +ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ pip install build \ - && pip wheel -r requirements.txt + && pip wheel \ + --wheel-dir=/tmp/wheels \ + -r requirements.txt # The final stage - slim version # ------------------------------ # This stage is built and published as jupyterhub/k8s-hub-slim. It is meant to -# provide no apt packages besides whats essential. +# provide no non-essential packages. # FROM python:3.9-slim-bullseye as slim-stage - -ARG NB_USER=jovyan -ARG NB_UID=1000 -ARG HOME=/home/jovyan - ENV DEBIAN_FRONTEND=noninteractive -RUN adduser --disabled-password \ +ARG NB_USER=jovyan \ + NB_UID=1000 \ + HOME=/home/jovyan +RUN adduser \ + --disabled-password \ --gecos "Default user" \ --uid ${NB_UID} \ --home ${HOME} \ @@ -49,14 +52,11 @@ RUN apt-get update \ tini \ && rm -rf /var/lib/apt/lists/* -# set pip's cache directory using this environment variable, and use -# ARG instead of ENV to ensure its only set when the image is built -ARG PIP_CACHE_DIR=/tmp/pip-cache - # install wheels built in the build-stage COPY requirements.txt /tmp/requirements.txt +ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ - --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ + --mount=type=cache,from=build-stage,source=/tmp/wheels,target=/tmp/wheels \ pip install \ --find-links=/tmp/wheels/ \ -r /tmp/requirements.txt @@ -72,10 +72,10 @@ CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] # The final stage - default version # --------------------------------- -# We add a few non-critical apt packages on top of the slim version to provide -# some debugging utility for k8s admins. +# We add a few non-critical packages on top of the slim version to provide some +# additional utility. # -FROM slim-stage +FROM slim-stage as default-stage USER root RUN apt-get update \ diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 5d1258462b..2b3be10c7d 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,36 +1,39 @@ # syntax = docker/dockerfile:1.3 -# The build stage -# --------------- -FROM python:3.9-bullseye as build-stage - # VULN_SCAN_TIME=2022-08-08_05:22:22 -WORKDIR /build-stage -# set pip's cache directory using this environment variable, and use -# ARG instead of ENV to ensure its only set when the image is built -ARG PIP_CACHE_DIR=/tmp/pip-cache +# The build stage +# --------------- +# This stage is building Python wheels for use in later stages by using a base +# image that has more pre-requisites to do so, such as a C++ compiler. +# +FROM python:3.9-bullseye as build-stage -# These are mounted into the final image for installation +# Build wheels +# +# We set pip's cache directory and expose it across build stages via an +# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). +# COPY requirements.txt requirements.txt +ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ pip install build \ - && pip wheel -r requirements.txt + && pip wheel \ + --wheel-dir=/tmp/wheels \ + -r requirements.txt # The final stage # --------------- - +# FROM python:3.9-slim-bullseye +ENV DEBIAN_FRONTEND=noninteractive -# VULN_SCAN_TIME= - -ENV DEBIAN_FRONTEND=noninteractive \ - NB_USER=jovyan \ +ENV NB_USER=jovyan \ NB_UID=1000 \ HOME=/home/jovyan - -RUN adduser --disabled-password \ +RUN adduser \ + --disabled-password \ --gecos "Default user" \ --uid ${NB_UID} \ --home ${HOME} \ @@ -48,14 +51,11 @@ RUN apt-get update \ git \ && rm -rf /var/lib/apt/lists/* -# set pip's cache directory using this environment variable, and use -# ARG instead of ENV to ensure its only set when the image is built -ARG PIP_CACHE_DIR=/tmp/pip-cache - # install wheels built in the build-stage COPY requirements.txt /tmp/requirements.txt +ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ - --mount=type=cache,from=build-stage,source=/build-stage,target=/tmp/wheels \ + --mount=type=cache,from=build-stage,source=/tmp/wheels,target=/tmp/wheels \ pip install \ --find-links=/tmp/wheels/ \ -r /tmp/requirements.txt From eb2fed8faf7d47a82e8d5e0c33d9f05e49710aa5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 30 Oct 2022 19:24:50 +0100 Subject: [PATCH 574/898] hub image: omit py-spy from slim image --- images/hub/Dockerfile | 18 +++++++++++++++++- images/hub/requirements.in | 5 ----- images/hub/requirements.txt | 2 -- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 6c00a051b3..d394b2e78f 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -20,7 +20,11 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ pip install build \ && pip wheel \ --wheel-dir=/tmp/wheels \ - -r requirements.txt + -r requirements.txt \ + # Additional wheels for default-stage. Updates below should be repeated + # in default-stage. + # + py-spy # The final stage - slim version @@ -78,6 +82,17 @@ CMD ["jupyterhub", "--config", "/usr/local/etc/jupyterhub/jupyterhub_config.py"] FROM slim-stage as default-stage USER root + +ARG PIP_CACHE_DIR=/tmp/pip-cache +RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + --mount=type=cache,from=build-stage,source=/tmp/wheels,target=/tmp/wheels \ + pip install \ + --find-links=/tmp/wheels/ \ + # Updates below should be repeated in build-stage. + # + # py-spy is useful for profiling performance of running hubs + py-spy + RUN apt-get update \ && apt-get install -y --no-install-recommends \ # misc network utilities @@ -88,4 +103,5 @@ RUN apt-get update \ less \ vim \ && rm -rf /var/lib/apt/lists/* + USER ${NB_USER} diff --git a/images/hub/requirements.in b/images/hub/requirements.in index a64e3648bb..47bc943d00 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -35,8 +35,3 @@ statsd # statsd metrics collection (TODO: remove soon, since folks use promethe # The idle culler service jupyterhub-idle-culler - -## Useful tools - -# py-spy is useful for profiling running hubs -py-spy diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 2c56fbf3e3..337ffb12f5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -124,8 +124,6 @@ prometheus-client==0.15.0 # via jupyterhub psycopg2-binary==2.9.4 # via -r requirements.in -py-spy==0.3.14 - # via -r requirements.in pyasn1==0.4.8 # via ldap3 pycparser==2.21 From 2e2369d58c83e3664511e7a52f19fcb38db3fc22 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 30 Oct 2022 19:46:06 +0100 Subject: [PATCH 575/898] docs: add documentation about k8s-hub-slim in security --- docs/source/administrator/security.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 3d74daf5d1..f1991ea580 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -167,6 +167,29 @@ security report generator. Use the following URL structure to test your domain: https://ssllabs.com/ssltest/analyze.html?d= ``` +## Minimize hub image + +By excluding non essential tools in the `hub` pod's image, you can avoid +exposure to vulnerabilities in those tools. You can use the slim version of the +hub image that we provide for this. + +```yaml +hub: + image: + # The slim variant excludes a few non-essential packages that are useful + # when debugging something from the hub pod. To use it, apply this + # configuration. + # + name: jupyterhub/k8s-hub-slim +``` + +```{note} +We are based on Linux Debian as a base image. There are container +scanners that pick up known vulnerabilities in Debian that the Debian security +team has dismissed. For details about this, see [this +comment](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/2918#issuecomment-1295813128). +``` + ## Secure access to Helm Helm 3 supports the security, identity, and authorization features of modern Kubernetes. Helm’s permissions are evaluated using your kubeconfig file. Cluster administrators can restrict user permissions at whatever granularity they see fit. From 590d6e8aceab4b408ca9f020cf65861027f075f9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 31 Oct 2022 01:25:09 +0100 Subject: [PATCH 576/898] Apply suggestions from code review Co-authored-by: Simon Li --- docs/source/administrator/security.md | 7 +++---- images/hub/Dockerfile | 2 -- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index f1991ea580..849ee8f9f8 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -167,11 +167,10 @@ security report generator. Use the following URL structure to test your domain: https://ssllabs.com/ssltest/analyze.html?d= ``` -## Minimize hub image +## Minimal hub image -By excluding non essential tools in the `hub` pod's image, you can avoid -exposure to vulnerabilities in those tools. You can use the slim version of the -hub image that we provide for this. +The default hub image includes some useful debugging tools. +You can use the slim version of image to minimise your exposure to vulnerabilities in those optional tools. ```yaml hub: diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index d394b2e78f..4a66932ab0 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -95,11 +95,9 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ RUN apt-get update \ && apt-get install -y --no-install-recommends \ - # misc network utilities curl \ dnsutils \ git \ - # misc other utilities less \ vim \ && rm -rf /var/lib/apt/lists/* From fd653547d28fdb9adfbfae6b8ee17f7cecd3c43a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 31 Oct 2022 05:34:06 +0000 Subject: [PATCH 577/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index f3781a09a0..ba3c8b2aa1 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -3,7 +3,7 @@ # --------------- FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-08-08_05:22:22 +# VULN_SCAN_TIME=2022-10-31_05:34:04 WORKDIR /build-stage From 3617066e825ae577cdebcbe584d2944cf349388e Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 31 Oct 2022 05:34:58 +0000 Subject: [PATCH 578/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 5d1258462b..abd8d2ec6d 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -3,7 +3,7 @@ # --------------- FROM python:3.9-bullseye as build-stage -# VULN_SCAN_TIME=2022-08-08_05:22:22 +# VULN_SCAN_TIME=2022-10-31_05:34:56 WORKDIR /build-stage @@ -23,7 +23,7 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ FROM python:3.9-slim-bullseye -# VULN_SCAN_TIME= +# VULN_SCAN_TIME=2022-10-31_05:34:56 ENV DEBIAN_FRONTEND=noninteractive \ NB_USER=jovyan \ From af04d41154ffcb0b93083563adf3928ee2a14f6f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Nov 2022 00:04:21 +0000 Subject: [PATCH 579/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.1.0 → v3.2.0](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.2.0) - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.3 → v3.0.0-alpha.4](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.3...v3.0.0-alpha.4) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f83a4b841b..fa5984a7c2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.0 hooks: - id: pyupgrade args: @@ -53,7 +53,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.3 + rev: v3.0.0-alpha.4 hooks: - id: prettier From ae5e3d2fb17b92c2e2f31634430a19b06b1e3ade Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 5 Nov 2022 11:07:40 +0000 Subject: [PATCH 580/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 16 ++++----- images/singleuser-sample/requirements.txt | 44 +++++++++++------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 337ffb12f5..8685fdd111 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -34,7 +34,7 @@ charset-normalizer==2.1.1 # via # aiohttp # requests -cryptography==38.0.1 +cryptography==38.0.3 # via pyopenssl escapism==1.0.1 # via @@ -44,7 +44,7 @@ frozenlist==1.3.1 # via # aiohttp # aiosignal -greenlet==1.1.3.post0 +greenlet==2.0.0.post0 # via sqlalchemy idna==3.4 # via @@ -56,7 +56,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.16.0 +jsonschema==4.17.0 # via # jupyter-telemetry # oauthenticator @@ -78,7 +78,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==4.2.0 +jupyterhub-kubespawner==4.3.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -122,7 +122,7 @@ pamela==1.0.0 # via jupyterhub prometheus-client==0.15.0 # via jupyterhub -psycopg2-binary==2.9.4 +psycopg2-binary==2.9.5 # via -r requirements.in pyasn1==0.4.8 # via ldap3 @@ -140,7 +140,7 @@ pyopenssl==22.1.0 # via certipy pyparsing==3.0.9 # via packaging -pyrsistent==0.18.1 +pyrsistent==0.19.2 # via jsonschema python-dateutil==2.8.2 # via @@ -175,7 +175,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.42 +sqlalchemy==1.4.43 # via # alembic # jupyterhub @@ -203,7 +203,7 @@ urllib3==1.26.12 # requests yarl==1.8.1 # via aiohttp -zipp==3.9.0 +zipp==3.10.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 3feded4b1d..90eb6bdf70 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -15,13 +15,13 @@ argon2-cffi==21.3.0 # notebook argon2-cffi-bindings==21.2.0 # via argon2-cffi -asttokens==2.0.8 +asttokens==2.1.0 # via stack-data async-generator==1.10 # via jupyterhub attrs==22.1.0 # via jsonschema -babel==2.10.3 +babel==2.11.0 # via jupyterlab-server backcall==0.2.0 # via ipython @@ -39,7 +39,7 @@ cffi==1.15.1 # cryptography charset-normalizer==2.1.1 # via requests -cryptography==38.0.1 +cryptography==38.0.3 # via pyopenssl debugpy==1.6.3 # via ipykernel @@ -49,11 +49,11 @@ defusedxml==0.7.1 # via nbconvert entrypoints==0.4 # via jupyter-client -executing==1.1.1 +executing==1.2.0 # via stack-data fastjsonschema==2.16.2 # via nbformat -greenlet==1.1.3.post0 +greenlet==2.0.0.post0 # via sqlalchemy idna==3.4 # via @@ -64,11 +64,11 @@ importlib-metadata==5.0.0 # jupyterhub # jupyterlab-server # nbconvert -ipykernel==6.16.1 +ipykernel==6.17.0 # via # nbclassic # notebook -ipython==8.5.0 +ipython==8.6.0 # via # ipykernel # jupyterlab @@ -89,12 +89,12 @@ jinja2==3.1.2 # notebook json5==0.9.10 # via jupyterlab-server -jsonschema==4.16.0 +jsonschema==4.17.0 # via # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.4.3 +jupyter-client==7.4.4 # via # ipykernel # jupyter-server @@ -128,7 +128,7 @@ jupyterlab==3.4.8 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.16.1 +jupyterlab-server==2.16.2 # via # jupyterlab # retrolab @@ -145,7 +145,7 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.4 # via nbconvert -nbclassic==0.4.5 +nbclassic==0.4.8 # via # -r requirements.in # jupyterlab @@ -153,7 +153,7 @@ nbclassic==0.4.5 # retrolab nbclient==0.7.0 # via nbconvert -nbconvert==7.2.2 +nbconvert==7.2.3 # via # jupyter-server # nbclassic @@ -174,11 +174,11 @@ nest-asyncio==1.5.6 # nbclassic # nbclient # notebook -notebook==6.5.1 +notebook==6.5.2 # via # jupyterlab # nbgitpuller -notebook-shim==0.2.0 +notebook-shim==0.2.2 # via nbclassic oauthlib==3.2.2 # via jupyterhub @@ -206,7 +206,7 @@ prometheus-client==0.15.0 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.31 +prompt-toolkit==3.0.32 # via ipython psutil==5.9.3 # via ipykernel @@ -226,7 +226,7 @@ pyopenssl==22.1.0 # via certipy pyparsing==3.0.9 # via packaging -pyrsistent==0.18.1 +pyrsistent==0.19.2 # via jsonschema python-dateutil==2.8.2 # via @@ -234,7 +234,7 @@ python-dateutil==2.8.2 # jupyterhub python-json-logger==2.0.4 # via jupyter-telemetry -pytz==2022.5 +pytz==2022.6 # via babel pyzmq==24.0.1 # via @@ -267,13 +267,13 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==1.4.42 +sqlalchemy==1.4.43 # via # alembic # jupyterhub -stack-data==0.5.1 +stack-data==0.6.0 # via ipython -terminado==0.16.0 +terminado==0.17.0 # via # jupyter-server # nbclassic @@ -317,7 +317,7 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.4.1 +websocket-client==1.4.2 # via jupyter-server -zipp==3.9.0 +zipp==3.10.0 # via importlib-metadata From 2c9ae270f5891ea6b05dc28c13c66ccce74779cd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 5 Nov 2022 13:16:21 +0100 Subject: [PATCH 581/898] Systematic update to python 3.11 --- .github/workflows/test-chart.yaml | 16 ++++++++-------- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 2 +- ci/refreeze | 2 +- images/hub/Dockerfile | 4 ++-- images/hub/requirements.txt | 8 +------- images/secret-sync/Dockerfile | 2 +- images/singleuser-sample/Dockerfile | 4 ++-- images/singleuser-sample/requirements.txt | 11 +---------- 11 files changed, 20 insertions(+), 35 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 13d02bbc0c..e904c6b6bd 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -28,12 +28,12 @@ on: jobs: lint_shell_scripts: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install dependencies run: pip install pre-commit @@ -42,12 +42,12 @@ jobs: run: pre-commit run --all --config .pre-commit-config-shellcheck.yaml lint_and_validate_rendered_templates: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install dependencies run: pip install chartpress yamllint @@ -60,7 +60,7 @@ jobs: continue-on-error: true lint_and_validate_templates_with_schema: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -80,7 +80,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install dependencies run: | @@ -114,7 +114,7 @@ jobs: continue-on-error: true test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 timeout-minutes: 20 strategy: @@ -235,7 +235,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" # Install a local ACME server to fill the role of Let's Encrypt (LE). We # do this as the HTTP challenge sent out by an ACME server must be able to diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index e99ec84d91..592632915e 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install chartpress run: pip install chartpress diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 23ee030345..a0a51a9184 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -34,7 +34,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install deps run: pip install -r docs/requirements.txt diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0b2048df79..8700844857 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -59,7 +59,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install chartpress run: | diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 099b41d95a..9f47048b2b 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -137,7 +137,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: "3.9" + python-version: "3.11" - name: Install Python dependencies run: pip install packaging requests diff --git a/ci/refreeze b/ci/refreeze index 5ba130cee6..13b46df7d9 100755 --- a/ci/refreeze +++ b/ci/refreeze @@ -13,7 +13,7 @@ for img in ${IMAGES}; do --volume="$PWD:/io" \ --workdir=/io \ --user=root \ - python:3.9-bullseye \ + python:3.11-bullseye \ sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' popd done diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 1b5f08ff2e..3e150a9963 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -7,7 +7,7 @@ # This stage is building Python wheels for use in later stages by using a base # image that has more pre-requisites to do so, such as a C++ compiler. # -FROM python:3.9-bullseye as build-stage +FROM python:3.11-bullseye as build-stage # Build wheels # @@ -32,7 +32,7 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ # This stage is built and published as jupyterhub/k8s-hub-slim. It is meant to # provide no non-essential packages. # -FROM python:3.9-slim-bullseye as slim-stage +FROM python:3.11-slim-bullseye as slim-stage ENV DEBIAN_FRONTEND=noninteractive ARG NB_USER=jovyan \ diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 8685fdd111..ba4e986a40 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with python 3.9 +# This file is autogenerated by pip-compile with python 3.11 # To update, run: # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml @@ -50,8 +50,6 @@ idna==3.4 # via # requests # yarl -importlib-metadata==5.0.0 - # via jupyterhub jinja2==3.1.2 # via # jupyterhub @@ -167,8 +165,6 @@ ruamel-yaml==0.17.21 # via # jupyter-telemetry # oauthenticator -ruamel-yaml-clib==0.2.7 - # via ruamel-yaml six==1.16.0 # via # kubernetes-asyncio @@ -203,8 +199,6 @@ urllib3==1.26.12 # requests yarl==1.8.1 # via aiohttp -zipp==3.10.0 - # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: # setuptools diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 9df0f545fb..095eaf336e 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.9-alpine +FROM python:3.11-alpine # VULN_SCAN_TIME=2022-10-10_06:01:28 diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 453a7d71bd..ccd66cc970 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -7,7 +7,7 @@ # This stage is building Python wheels for use in later stages by using a base # image that has more pre-requisites to do so, such as a C++ compiler. # -FROM python:3.9-bullseye as build-stage +FROM python:3.11-bullseye as build-stage # Build wheels # @@ -26,7 +26,7 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ # The final stage # --------------- # -FROM python:3.9-slim-bullseye +FROM python:3.11-slim-bullseye ENV DEBIAN_FRONTEND=noninteractive ENV NB_USER=jovyan \ diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 90eb6bdf70..4b4e7823ba 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with python 3.9 +# This file is autogenerated by pip-compile with python 3.11 # To update, run: # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml @@ -59,11 +59,6 @@ idna==3.4 # via # anyio # requests -importlib-metadata==5.0.0 - # via - # jupyterhub - # jupyterlab-server - # nbconvert ipykernel==6.17.0 # via # nbclassic @@ -251,8 +246,6 @@ retrolab==0.3.21 # via -r requirements.in ruamel-yaml==0.17.21 # via jupyter-telemetry -ruamel-yaml-clib==0.2.7 - # via ruamel-yaml send2trash==1.8.0 # via # jupyter-server @@ -319,5 +312,3 @@ webencodings==0.5.1 # tinycss2 websocket-client==1.4.2 # via jupyter-server -zipp==3.10.0 - # via importlib-metadata From 099eb497ba598c5327f023712c353ce1b78abccd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Nov 2022 05:01:19 +0000 Subject: [PATCH 582/898] build(deps): bump aquasecurity/trivy-action from 0.7.1 to 0.8.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.7.1 to 0.8.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/d63413b0a4a4482237085319f7f4a1ce99a8f2ac...9ab158e8597f3b310480b9a69402b419bc03dbd5) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0b2048df79..347fe0b2fe 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac + uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac + uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@d63413b0a4a4482237085319f7f4a1ce99a8f2ac + uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 with: image-ref: rebuilt-image format: table From a729691bd4e724e171a1755d4fc6f3cd29c1f5c1 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 9 Nov 2022 18:31:00 +0000 Subject: [PATCH 583/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 8 ++++---- images/singleuser-sample/requirements.txt | 16 +++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index ba4e986a40..1d0088766d 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -6,7 +6,7 @@ # aiohttp==3.8.3 # via kubernetes-asyncio -aiosignal==1.2.0 +aiosignal==1.3.1 # via aiohttp alembic==1.8.1 # via jupyterhub @@ -40,11 +40,11 @@ escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator -frozenlist==1.3.1 +frozenlist==1.3.3 # via # aiohttp # aiosignal -greenlet==2.0.0.post0 +greenlet==2.0.1 # via sqlalchemy idna==3.4 # via @@ -178,7 +178,7 @@ sqlalchemy==1.4.43 # sqlalchemy-cockroachdb sqlalchemy-cockroachdb==1.4.4 # via -r requirements.in -statsd==3.3.0 +statsd==4.0.1 # via -r requirements.in text-unidecode==1.3 # via python-slugify diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 4b4e7823ba..c11ccf0c7d 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -53,13 +53,13 @@ executing==1.2.0 # via stack-data fastjsonschema==2.16.2 # via nbformat -greenlet==2.0.0.post0 +greenlet==2.0.1 # via sqlalchemy idna==3.4 # via # anyio # requests -ipykernel==6.17.0 +ipykernel==6.17.1 # via # nbclassic # notebook @@ -96,7 +96,7 @@ jupyter-client==7.4.4 # nbclassic # nbclient # notebook -jupyter-core==4.11.2 +jupyter-core==5.0.0 # via # jupyter-client # jupyter-server @@ -105,7 +105,7 @@ jupyter-core==4.11.2 # nbconvert # nbformat # notebook -jupyter-server==1.21.0 +jupyter-server==1.23.1 # via # jupyterlab # jupyterlab-server @@ -148,7 +148,7 @@ nbclassic==0.4.8 # retrolab nbclient==0.7.0 # via nbconvert -nbconvert==7.2.3 +nbconvert==7.2.4 # via # jupyter-server # nbclassic @@ -160,7 +160,7 @@ nbformat==5.7.0 # nbclient # nbconvert # notebook -nbgitpuller==1.1.0 +nbgitpuller==1.1.1 # via -r requirements.in nest-asyncio==1.5.6 # via @@ -195,6 +195,8 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython +platformdirs==2.5.3 + # via jupyter-core prometheus-client==0.15.0 # via # jupyter-server @@ -203,7 +205,7 @@ prometheus-client==0.15.0 # notebook prompt-toolkit==3.0.32 # via ipython -psutil==5.9.3 +psutil==5.9.4 # via ipykernel ptyprocess==0.7.0 # via From 318da8e559e1cb43d9d0df538c0694d001258aad Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 10 Nov 2022 05:20:53 +0000 Subject: [PATCH 584/898] Update kube-scheduler version from v1.23.13 to v1.23.14 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 453f00a4d8..52f6d91a2a 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.13" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.23.14" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From c1abc4b9254015142fdebc3e99de767dc42fd7e6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Nov 2022 14:37:58 +0100 Subject: [PATCH 585/898] Drop support for k8s 1.20 --- .github/workflows/test-chart.yaml | 4 +- jupyterhub/Chart.yaml | 2 +- jupyterhub/templates/hub/pdb.yaml | 5 -- jupyterhub/templates/proxy/autohttps/pdb.yaml | 5 -- jupyterhub/templates/proxy/pdb.yaml | 5 -- .../scheduling/user-placeholder/pdb.yaml | 5 -- .../scheduling/user-scheduler/configmap.yaml | 65 ++----------------- .../scheduling/user-scheduler/deployment.yaml | 17 ----- .../scheduling/user-scheduler/pdb.yaml | 5 -- .../scheduling/user-scheduler/rbac.yaml | 8 +-- 10 files changed, 12 insertions(+), 109 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index e904c6b6bd..e39cb3817b 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -135,12 +135,12 @@ jobs: test: install local-chart-extra-args: >- --set hub.image.name=jupyterhub/k8s-hub-slim - - k3s-channel: v1.21 # also test prePuller.hook + - k3s-channel: v1.22 # also test prePuller.hook test: install local-chart-extra-args: >- --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - - k3s-channel: v1.20 # also test hub.existingSecret + - k3s-channel: v1.21 # also test hub.existingSecret test: install local-chart-extra-args: >- --set hub.existingSecret=test-hub-existing-secret diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index ef529f5250..7e2a4489bf 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -8,7 +8,7 @@ keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg -kubeVersion: ">=1.20.0-0" +kubeVersion: ">=1.21.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers # listed, we have added some below, but in practice the entire JupyterHub team diff --git a/jupyterhub/templates/hub/pdb.yaml b/jupyterhub/templates/hub/pdb.yaml index 3a22e39bdb..5b69eb4311 100644 --- a/jupyterhub/templates/hub/pdb.yaml +++ b/jupyterhub/templates/hub/pdb.yaml @@ -1,10 +1,5 @@ {{- if .Values.hub.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} kind: PodDisruptionBudget metadata: name: {{ include "jupyterhub.hub.fullname" . }} diff --git a/jupyterhub/templates/proxy/autohttps/pdb.yaml b/jupyterhub/templates/proxy/autohttps/pdb.yaml index 074d0ac51b..fe6c692134 100644 --- a/jupyterhub/templates/proxy/autohttps/pdb.yaml +++ b/jupyterhub/templates/proxy/autohttps/pdb.yaml @@ -1,10 +1,5 @@ {{- if .Values.proxy.traefik.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} kind: PodDisruptionBudget metadata: name: proxy diff --git a/jupyterhub/templates/proxy/pdb.yaml b/jupyterhub/templates/proxy/pdb.yaml index d8651f56ac..6262f8e4de 100644 --- a/jupyterhub/templates/proxy/pdb.yaml +++ b/jupyterhub/templates/proxy/pdb.yaml @@ -1,10 +1,5 @@ {{- if .Values.proxy.chp.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} kind: PodDisruptionBudget metadata: name: {{ include "jupyterhub.proxy.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml index ec84fb58ce..85e64c968b 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml @@ -3,12 +3,7 @@ The cluster autoscaler should be allowed to evict and reschedule these pods if it would help in order to scale down a node. */}} {{- if .Values.scheduling.userPlaceholder.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} kind: PodDisruptionBudget metadata: name: {{ include "jupyterhub.user-placeholder.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml index 22ea9a8265..f717e76a42 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -11,31 +11,16 @@ data: user-scheduler pod. ref: https://kubernetes.io/docs/reference/scheduling/config/ - ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta2/ ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1/ - v1beta1 can be used with kube-scheduler binary version <=1.21 - v1beta2 requires kube-scheduler binary version >=1.22 - v1beta3 requires kube-scheduler binary version >=1.23 + kubescheduler.config.k8s.io/v1beta3 requires kube-scheduler binary version >=1.23 + kubescheduler.config.k8s.io/v1 requires kube-scheduler binary version >=1.25 - kube-scheduler binaries versioned >=1.21 will error in k8s clusters - versioned <=1.20. To support a modern version of kube-scheduler and k8s - versions <=1.20 upwards, we provide two scenarios: - - 1. For k8s >= 1.21 we use a modern version of kube-scheduler and Helm chart - configuration works as expected. - 2. For k8s <= 1.20 we use a hardcoded version of kube-scheduler (v1.20.15) - and configuration (v1beta1) of kube-scheduler. + FIXME: Add logic to transition from v1beta3 to v1 if its supported. + v1beta3 is likeley to be removed in k8s 1.27. */}} config.yaml: | - {{- /* - FIXME: We have added a workaround for EKS where - .Capabilities.KubeVersion.Minor can return a - string like "22+" instead of just "22". - - See https://github.com/aws/eks-distro/issues/1128. - */}} - {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} apiVersion: kubescheduler.config.k8s.io/v1beta3 kind: KubeSchedulerConfiguration leaderElection: @@ -52,44 +37,4 @@ data: pluginConfig: {{- . | toYaml | nindent 10 }} {{- end }} - {{- else }} - # WARNING: The tag of this image is hardcoded, and the - # "scheduling.userScheduler.plugins" configuration of the Helm - # chart that generated this resource manifest wasn't respected. If - # you install the Helm chart in a k8s cluster versioned 1.21 or - # higher, your configuration will be respected. - apiVersion: kubescheduler.config.k8s.io/v1beta1 - kind: KubeSchedulerConfiguration - leaderElection: - resourceLock: endpoints - resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} - resourceNamespace: "{{ .Release.Namespace }}" - profiles: - - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} - plugins: - score: - disabled: - - name: SelectorSpread - - name: TaintToleration - - name: PodTopologySpread - - name: NodeResourcesBalancedAllocation - - name: NodeResourcesLeastAllocated - # Disable plugins to be allowed to enable them again with a - # different weight and avoid an error. - - name: NodePreferAvoidPods - - name: NodeAffinity - - name: InterPodAffinity - - name: ImageLocality - enabled: - - name: NodePreferAvoidPods - weight: 161051 - - name: NodeAffinity - weight: 14631 - - name: InterPodAffinity - weight: 1331 - - name: NodeResourcesMostAllocated - weight: 121 - - name: ImageLocality - weight: 11 - {{- end }} {{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index 58bb23abe1..b021c17de8 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -50,24 +50,7 @@ spec: {{- end }} containers: - name: kube-scheduler - {{- /* - FIXME: We have added a workaround for EKS where - .Capabilities.KubeVersion.Minor can return a - string like "22+" instead of just "22". - - See https://github.com/aws/eks-distro/issues/1128. - */}} - {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} - {{- else }} - # WARNING: The tag of this image is hardcoded, and the - # "scheduling.userScheduler.image.tag" configuration of the - # Helm chart that generated this resource manifest isn't - # respected. If you install the Helm chart in a k8s cluster - # versioned 1.21 or higher, your configuration will be - # respected. - image: {{ .Values.scheduling.userScheduler.image.name }}:v1.20.15 - {{- end }} {{- with .Values.scheduling.userScheduler.image.pullPolicy }} imagePullPolicy: {{ . }} {{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml index 3a9544ef2e..d12e99b869 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml @@ -1,10 +1,5 @@ {{- if and .Values.scheduling.userScheduler.enabled .Values.scheduling.userScheduler.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} kind: PodDisruptionBudget metadata: name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index f77640b146..01bfc78ba2 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -19,9 +19,9 @@ rules: # - unchanged between 1.18 and 1.20 # - changed in 1.21: get/list/watch permission for namespace, # csidrivers, csistoragecapacities was added. - # - unchanged between 1.22 and 1.23 + # - unchanged between 1.22 and 1.25 # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L705-L861 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.25.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L730-L886 - apiGroups: - "" - events.k8s.io @@ -183,9 +183,9 @@ rules: # Copied from the system:volume-scheduler ClusterRole of the k8s version # matching the kube-scheduler binary we use. # - # NOTE: These rules have not changed between 1.12 and 1.23. + # NOTE: These rules have not changed between 1.12 and 1.25. # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1280-L1307 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.25.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1305-L1332 - apiGroups: - "" resources: From 92f10a27bba5642b6fbe76882e2a43d9ae01ee38 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Nov 2022 15:34:16 +0100 Subject: [PATCH 586/898] Add brief note in changelog about breaking change --- docs/source/changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index cb690459a7..c9103c6a5f 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,6 +12,9 @@ and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. +- K8s 1.21 is now required. +- The Helm chart's provided images now use Python 3.11 instead of Python 3.9. + ## 2.0 ### 2.0.0 - 2022-09-09 From 6040054de0261d530debb8f01f0de911169e95cd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Nov 2022 18:07:22 +0100 Subject: [PATCH 587/898] ci: minimize yamllint-config.yaml's complexity --- tools/templates/yamllint-config.yaml | 65 ++-------------------------- 1 file changed, 3 insertions(+), 62 deletions(-) diff --git a/tools/templates/yamllint-config.yaml b/tools/templates/yamllint-config.yaml index 82b2ae45cf..ade69db121 100644 --- a/tools/templates/yamllint-config.yaml +++ b/tools/templates/yamllint-config.yaml @@ -1,67 +1,8 @@ rules: - braces: - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: -1 - brackets: - min-spaces-inside: 0 - max-spaces-inside: 0 - min-spaces-inside-empty: -1 - max-spaces-inside-empty: -1 - colons: - max-spaces-before: 0 - max-spaces-after: 1 - commas: - max-spaces-before: 0 - min-spaces-after: 0 - max-spaces-after: 1 - comments: - require-starting-space: false # Default: true (*) - min-spaces-from-content: 2 - comments-indentation: {} - document-end: disable - document-start: disable # Default: { present: true } - empty-lines: - max: 2 - max-start: 0 - max-end: 0 - empty-values: - forbid-in-block-mappings: false - forbid-in-flow-mappings: false - hyphens: - max-spaces-after: 1 indentation: spaces: 2 # Default: consistent - indent-sequences: whatever # Default true (**) - check-multi-line-strings: false - key-duplicates: enable - key-ordering: disable + indent-sequences: whatever # Default true (*) line-length: disable # Default: { max: 80, ... } - new-line-at-end-of-file: enable - new-lines: - type: unix - octal-values: - forbid-implicit-octal: false - forbid-explicit-octal: false - trailing-spaces: disable - truthy: - level: warning -# (*) Until we can use PR: https://github.com/kubernetes/helm/pull/3811 -# (**) toYaml forces us to use false and be indent different amounts of spaced -# depending on importing lists or dicts. For all fields, where we support both -# lists and dicts, this would be a hassle. -# Consider this case... - -# # values.yaml -# list: -# - key1: value1 -# dict: -# key1: value1 -# -# # template.yaml -# myList: -# {{- .Values.list | toYaml | nindent 0 }} -# myDict: -# {{- .Values.dict | toYaml | nindent 2 }} +# (*) toYaml's emitted sequences/lists will have no indentation and we have no +# control over it, so we compromise by setting "whatever". From b5c224002b41395f1e160b47f9097f71329ee0b0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Nov 2022 21:02:53 +0100 Subject: [PATCH 588/898] ci: minor refactoring of tools --- tools/compare-values-schema-content.py | 105 ++++++++++++++----------- tools/generate-json-schema.py | 1 - tools/templates/lint-and-validate.py | 31 ++------ tools/validate-against-schema.py | 1 - 4 files changed, 65 insertions(+), 73 deletions(-) diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py index d66f35c04f..61e3b754d3 100755 --- a/tools/compare-values-schema-content.py +++ b/tools/compare-values-schema-content.py @@ -1,48 +1,50 @@ #!/usr/bin/env python3 """ -This script is meant to assist in a manual validation that the content of -schema.yaml covers values.yaml, and vice versa. +This script is meant to help compare the entries in schema.yaml with the entries +in values.yaml and lint-and-validate-values.yaml. -FIXME: It would be nice to run this as part of our CI pipeline to report if - schema.yaml and values.yaml gets out of sync, but first we need to - address what it means to be out of sync. +Running this script can result in output like: - Consider if schema.yaml describes extraLabels, and we in this helm chart - have an extra label set in values, how should our comparison realize that - its nothing to bother about? - - That kind of complexity is currently an issue for labels, resources, - containerSecurityContext, readiness- and livenessProbe's, and hub.config. + schema.yaml entries not found in values.yaml: + - hub.deploymentStrategy.rollingUpdate + - hub.fsGid + - rbac.enabled """ import os -import sys from collections.abc import MutableMapping -import jsonschema import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "schema.yaml") values_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.yaml") -lint_and_validate_values_yaml = os.path.join( - here_dir, "templates", "lint-and-validate-values.yaml" -) +lint_values_yaml = os.path.join(here_dir, "templates", "lint-and-validate-values.yaml") def reduce_schema(d): """ Takes a jsonschema loaded as a dictionary and return a reduced structure ignoring everything apart from the structure it describes. + + If additionalProperties or patternProperties is set, a "*" key will be added + instead of whats listed under "properties". """ r = {} - CONTAINS_KEYS = "properties" - if CONTAINS_KEYS in d: - for k, v in d[CONTAINS_KEYS].items(): - if isinstance(v, MutableMapping) and v.get(CONTAINS_KEYS): - r[k] = reduce_schema(v) - else: - r[k] = None + if "properties" in d: + for k, v in d["properties"].items(): + r[k] = None + if isinstance(v, MutableMapping): + if v.get("additionalProperties") or v.get("patternProperties"): + r[k] = {"*": None} + # Exception: The schema describes conditional properties based + # on a condition in an "if" key. Then assume we want + # the properties found in the "then" key. + # + elif v.get("then", {}).get("properties"): + r[k] = reduce_schema(v["then"]) + elif v.get("properties"): + r[k] = reduce_schema(v) return r @@ -56,42 +58,55 @@ def flatten(d, parent_key="", sep="."): new_key = parent_key + sep + k if parent_key else k if isinstance(v, MutableMapping): if v: + items.append(new_key) items.extend(flatten(v, parent_key=new_key, sep=sep)) else: items.append(new_key) else: items.append(new_key) if not parent_key: - return set(items) - else: - return items + items = set(items) + return items + + +def startswith_any_element_in_list(string, in_list): + for s in in_list: + if string.startswith(s): + return True + return False + + +def get_schema_values_diff(values_file, schema, schema_wildcards): + with open(values_file) as f: + values = yaml.safe_load(f) + values = flatten(values) + return { + v + for v in schema - values + if not startswith_any_element_in_list(v, schema_wildcards) + } def run(): - # Using these sets, we can validate further manually by printing the results - # of set operations. with open(schema_yaml) as f: schema = yaml.safe_load(f) - with open(values_yaml) as f: - values = yaml.safe_load(f) - # with open(lint_and_validate_values_yaml) as f: - # lint_and_validate_values = yaml.safe_load(f) schema = flatten(reduce_schema(schema)) - values = flatten(values) - # lint_and_validate_values = flatten(lint_and_validate_values) + schema_wildcards = {l[:-2] for l in schema if l.endswith(".*")} + schema = {l for l in schema if not l.endswith(".*")} - print( - "The keys from values.yaml minus those from schema.yaml:\n", - "\n".join(sorted(values - schema)), - "\n\n", - sep="\n", - ) - print( - "The keys from schema.yaml minus those from values.yaml:\n", - "\n".join(sorted(schema - values)), - "\n\n", - sep="\n", + schema_values_diff = get_schema_values_diff(values_yaml, schema, schema_wildcards) + if schema_values_diff: + print("schema.yaml entries not found in values.yaml:") + for l in sorted(schema_values_diff): + print(f"- {l}") + + lint_schema_values_diff = get_schema_values_diff( + lint_values_yaml, schema, schema_wildcards ) + if lint_schema_values_diff: + print("schema.yaml entries not found in lint-and-validate-values.yaml:"), + for l in sorted(lint_schema_values_diff): + print(f"- {l}") run() diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index 8c4396c0cf..f4d6286dcc 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -11,7 +11,6 @@ import json import os -import sys from collections.abc import MutableMapping import yaml diff --git a/tools/templates/lint-and-validate.py b/tools/templates/lint-and-validate.py index e0941a1de4..05b1b2fbb9 100755 --- a/tools/templates/lint-and-validate.py +++ b/tools/templates/lint-and-validate.py @@ -15,7 +15,6 @@ """ import argparse -import glob import os import pipes import subprocess @@ -43,30 +42,12 @@ def lint(yamllint_config, values, output_dir, strict, debug): """Calls `helm lint`, `helm template`, and `yamllint`.""" print("### Clearing output directory") - check_call( - [ - "mkdir", - "-p", - output_dir, - ] - ) - check_call( - [ - "rm", - "-rf", - output_dir + "/*", - ] - ) + check_call(["mkdir", "-p", output_dir]) + check_call(["rm", "-rf", f"{output_dir}/*"]) print("### Linting started") print("### 1/3 - helm lint: lint helm templates") - helm_lint_cmd = [ - "helm", - "lint", - "../../jupyterhub", - "--values", - values, - ] + helm_lint_cmd = ["helm", "lint", "../../jupyterhub", f"--values={values}"] if strict: helm_lint_cmd.append("--strict") if debug: @@ -78,10 +59,8 @@ def lint(yamllint_config, values, output_dir, strict, debug): "helm", "template", "../../jupyterhub", - "--values", - values, - "--output-dir", - output_dir, + f"--values={values}", + f"--output-dir={output_dir}", ] if debug: helm_template_cmd.append("--debug") diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index c397994a7c..658f7feee7 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 import os -import sys import jsonschema import yaml From cc59f8c57432fbb13fbcc4dd8c4921baf93f81e4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Nov 2022 22:24:39 +0100 Subject: [PATCH 589/898] pre-commit: add flake8 and fix details --- .pre-commit-config.yaml | 14 ++++++++++++++ images/secret-sync/acme-secret-sync.py | 5 +---- jupyterhub/files/hub/jupyterhub_config.py | 1 - tests/conftest.py | 1 - tests/test_hub.py | 5 ++--- tests/test_spawn.py | 4 ++-- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fa5984a7c2..ad4a49e7c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -62,3 +62,17 @@ repos: rev: 2.1.0 hooks: - id: chartpress + + # Linting: Python code (see the file .flake8) + - repo: https://github.com/PyCQA/flake8 + rev: "5.0.4" + hooks: + - id: flake8 + # Ignore style and complexity + # E: style errors + # W: style warnings + # C: complexity + # + args: + - --ignore=E,C,W + - --builtins=c diff --git a/images/secret-sync/acme-secret-sync.py b/images/secret-sync/acme-secret-sync.py index b06cf7e27d..034daaea7f 100755 --- a/images/secret-sync/acme-secret-sync.py +++ b/images/secret-sync/acme-secret-sync.py @@ -36,13 +36,10 @@ """ import argparse import base64 -import io import json import logging import os -import subprocess import sys -import tarfile import time from kubernetes import client, config @@ -65,7 +62,7 @@ def update_secret(namespace, secret_name, labels, key, value): secret = client.V1Secret( metadata=client.V1ObjectMeta(name=secret_name, labels=labels), data={} ) - resp = v1.create_namespaced_secret(namespace=namespace, body=secret) + v1.create_namespaced_secret(namespace=namespace, body=secret) logging.info(f"Created secret {secret_name} since it does not exist") else: raise diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 1ce7412471..6fe2e90b94 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -2,7 +2,6 @@ import os import re import sys -from binascii import a2b_hex from jupyterhub.utils import url_path_join from kubernetes_asyncio import client diff --git a/tests/conftest.py b/tests/conftest.py index 69dddf7f29..e855cf1f40 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,7 +6,6 @@ import os import textwrap import uuid -from urllib.parse import urlparse import pytest import requests diff --git a/tests/test_hub.py b/tests/test_hub.py index cdc549cce4..19fdfc2d47 100644 --- a/tests/test_hub.py +++ b/tests/test_hub.py @@ -156,7 +156,7 @@ def test_extra_files(extra_files_test_command): ) assert ( c.returncode == 0 - ), f"The hub.extraFiles configuration doesn't seem to have been honored!" + ), "The hub.extraFiles configuration doesn't seem to have been honored!" def test_load_etc_jupyterhub_d(): @@ -178,7 +178,7 @@ def test_load_etc_jupyterhub_d(): ) assert ( c.returncode == 0 - ), f"The hub.extraFiles configuration should have mounted a config file to /usr/local/etc/jupyterhub/jupyterhub_config.d which should have been loaded to write a dummy file for us!" + ), "The hub.extraFiles configuration should have mounted a config file to /usr/local/etc/jupyterhub/jupyterhub_config.d which should have been loaded to write a dummy file for us!" def test_load_existing_secret(): @@ -231,7 +231,6 @@ def test_load_existing_secret(): if "hub.existingSecret=None" in hub_logs: pytest.skip("hub.existingSecret is None") else: - k8s_secret_exist = False match = re.compile(r".*hub.existingSecret=(?P[\S]*).*", re.DOTALL).match( hub_logs ) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 525b823bff..bd3f7ccdda 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -51,7 +51,7 @@ def test_spawn_basic( ) assert ( c.returncode == 0 - ), f"singleuser.extraEnv didn't lead to a mounted environment variable!" + ), "singleuser.extraEnv didn't lead to a mounted environment variable!" # check user pod's extra files c = subprocess.run( @@ -67,7 +67,7 @@ def test_spawn_basic( ) assert ( c.returncode == 0 - ), f"The singleuser.extraFiles configuration doesn't seem to have been honored!" + ), "The singleuser.extraFiles configuration doesn't seem to have been honored!" finally: _delete_server(api_request, jupyter_user, request_data["test_timeout"]) From 65df9c3bfadb21222210877b6e4cd3d6ce54ed83 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Nov 2022 15:29:22 +0100 Subject: [PATCH 590/898] Update kube-scheduler to v1.25 and its configuration api to v1 --- .github/workflows/watch-dependencies.yaml | 2 +- .../scheduling/user-scheduler/configmap.yaml | 23 +++++++++++-------- .../scheduling/user-scheduler/deployment.yaml | 10 ++++++++ jupyterhub/values.yaml | 2 +- tools/templates/lint-and-validate-values.yaml | 2 +- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 9f47048b2b..aebb8e50e2 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -68,7 +68,7 @@ jobs: registry: registry.k8s.io repository: kube-scheduler values_path: scheduling.userScheduler.image.tag - version_startswith: "v1.23" + version_startswith: "v1.25" version_patch_regexp_group_suffix: "" - name: pause diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml index f717e76a42..0f142b01ff 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -8,23 +8,28 @@ metadata: data: {{- /* This is configuration of a k8s official kube-scheduler binary running in the - user-scheduler pod. + user-scheduler. - ref: https://kubernetes.io/docs/reference/scheduling/config/ - ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ - ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1/ + The config version and kube-scheduler binary version has a fallback for k8s + clusters versioned v1.23 or lower because: - kubescheduler.config.k8s.io/v1beta3 requires kube-scheduler binary version >=1.23 - kubescheduler.config.k8s.io/v1 requires kube-scheduler binary version >=1.25 + - v1 / v1beta3 config requires kube-scheduler binary >=1.25 / >=1.23 + - kube-scheduler binary >=1.25 requires storage.k8s.io/v1/CSIStorageCapacity + available first in k8s >=1.24 - FIXME: Add logic to transition from v1beta3 to v1 if its supported. - v1beta3 is likeley to be removed in k8s 1.27. + ref: https://kubernetes.io/docs/reference/scheduling/config/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ */}} config.yaml: | + {{- if semverCompare ">=1.24.0-0" .Capabilities.KubeVersion.Version }} + apiVersion: kubescheduler.config.k8s.io/v1 + {{- else }} apiVersion: kubescheduler.config.k8s.io/v1beta3 + {{- end }} kind: KubeSchedulerConfiguration leaderElection: - resourceLock: endpoints + resourceLock: endpointsleases resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} resourceNamespace: "{{ .Release.Namespace }}" profiles: diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index b021c17de8..5baf4f4e8d 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -50,7 +50,17 @@ spec: {{- end }} containers: - name: kube-scheduler + {{- if semverCompare ">=1.24.0-0" .Capabilities.KubeVersion.Version }} image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} + {{- else }} + # WARNING: The tag of this image is hardcoded, and the + # "scheduling.userScheduler.image.tag" configuration of the + # Helm chart that generated this resource manifest isn't + # respected. If you install the Helm chart in a k8s cluster + # versioned 1.24 or higher, your configuration will be + # respected. + image: {{ .Values.scheduling.userScheduler.image.name }}:v1.23.14 + {{- end }} {{- with .Values.scheduling.userScheduler.image.pullPolicy }} imagePullPolicy: {{ . }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 52f6d91a2a..08aefe7887 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.23.14" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.4" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 4dda6ce095..694f6f37ce 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -477,7 +477,7 @@ scheduling: plugins: score: disabled: - - name: SelectorSpread + - name: PodTopologySpread enabled: - name: NodePreferAvoidPods weight: 161051 From 557c96e6f2a0344c9682efc583fce718e547700a Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 14 Nov 2022 10:14:03 +0000 Subject: [PATCH 591/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 1d0088766d..e80e1555d8 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -171,7 +171,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.43 +sqlalchemy==1.4.44 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index c11ccf0c7d..6a99811e10 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -89,7 +89,7 @@ jsonschema==4.17.0 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.4.4 +jupyter-client==7.4.5 # via # ipykernel # jupyter-server @@ -123,7 +123,7 @@ jupyterlab==3.4.8 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.16.2 +jupyterlab-server==2.16.3 # via # jupyterlab # retrolab @@ -195,7 +195,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==2.5.3 +platformdirs==2.5.4 # via jupyter-core prometheus-client==0.15.0 # via @@ -262,11 +262,11 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==1.4.43 +sqlalchemy==1.4.44 # via # alembic # jupyterhub -stack-data==0.6.0 +stack-data==0.6.1 # via ipython terminado==0.17.0 # via From 4e136abb63cd732bb166503a0b6e7224e8102568 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 14 Nov 2022 13:10:40 +0100 Subject: [PATCH 592/898] ci: fix deprecation of set-output in github workflows --- .github/workflows/vuln-scan.yaml | 16 ++++++++-------- .github/workflows/watch-dependencies.yaml | 10 ++++++---- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 3a1e7acc98..54a519e139 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -77,9 +77,9 @@ jobs: ) echo "Identified image: $IMAGE_SPEC" - echo "::set-output name=spec::$IMAGE_SPEC" - echo "::set-output name=name::$(echo $IMAGE_SPEC | sed 's/\(.*\):.*/\1/')" - echo "::set-output name=tag::$(echo $IMAGE_SPEC | sed 's/.*:\(.*\)/\1/')" + echo "spec=$IMAGE_SPEC" >> $GITHUB_OUTPUT + echo "name=$(echo $IMAGE_SPEC | sed 's/\(.*\):.*/\1/')" >> $GITHUB_OUTPUT + echo "tag=$(echo $IMAGE_SPEC | sed 's/.*:\(.*\)/\1/')" >> $GITHUB_OUTPUT - name: Create ./tmp dir run: mkdir ./tmp @@ -126,7 +126,7 @@ jobs: id: analyze if: steps.rebuild.outcome == 'success' run: | - echo "::set-output name=utc_time::$(date --utc +'%F_%T')" + echo "utc_time=$(date --utc +'%F_%T')" >> $GITHUB_OUTPUT json_to_misc() { # Count vulnerabilities @@ -148,13 +148,13 @@ jobs: TMP="${TMP//'%'/'%25'}" TMP="${TMP//$'\n'/'%0A'}" TMP="${TMP//$'\r'/'%0D'}" - echo "::set-output name=md_summary_$1::$TMP" + echo "md_summary_$1=$TMP" >> $GITHUB_OUTPUT # Calculate a hash of the markdown summary HASH=$(cat tmp/md_summary_$1.md | sha1sum) HASH=${HASH:0:10} export HASH_$1=$HASH - echo "::set-output name=hash_$1::$HASH" + echo "hash_$1=$HASH" >> $GITHUB_OUTPUT } json_to_misc 1 @@ -162,10 +162,10 @@ jobs: # Did rebuilding the image change anything? if [ "$HASH_1" == "$HASH_2" ]; then - echo "::set-output name=proceed::no" + echo "proceed=no" >> $GITHUB_OUTPUT echo "No vulnerabilities were patched by rebuilding the image - won't proceed!" else - echo "::set-output name=proceed::yes" + echo "proceed=yes" >> $GITHUB_OUTPUT echo "Vulnerabilities were patched by rebuilding the image - will proceed!" fi diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index aebb8e50e2..2b464ff9c6 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -85,7 +85,7 @@ jobs: id: local run: | local_tag=$(cat jupyterhub/values.yaml | yq e '.${{ matrix.values_path }}' -) - echo "::set-output name=tag::$local_tag" + echo "tag=$local_tag" >> $GITHUB_OUTPUT - name: Get latest tag of ${{ matrix.registry }}/${{ matrix.repository }} id: latest @@ -100,7 +100,7 @@ jobs: docker run --rm quay.io/skopeo/stable list-tags docker://${{ matrix.registry }}/${{ matrix.repository }} \ | jq -r '[.Tags[] | select(. | match("^v?\\d+\\.\\d+(\\.\\d+)${{ matrix.version_patch_regexp_group_suffix }}$") | .string | startswith("${{ matrix.version_startswith }}"))] | sort_by(split(".") | map(tonumber? // (.[1:] | tonumber))) | last' ) - echo "::set-output name=tag::$latest_tag" + echo "tag=$latest_tag" >> $GITHUB_OUTPUT - name: Update values.yaml pinned tag if: steps.local.outputs.tag != steps.latest.outputs.tag @@ -146,12 +146,13 @@ jobs: id: local run: | local_version=$(cat images/hub/requirements.in | grep 'jupyterhub==' | sed 's/jupyterhub==//') - echo "::set-output name=version::$local_version" + echo "version=$local_version" >> $GITHUB_OUTPUT - name: Get latest version of jupyterhub id: latest shell: python run: | + import os import packaging.version import requests @@ -160,7 +161,8 @@ jobs: releases = data["releases"] latest_version = sorted(releases.keys(), key=packaging.version.Version)[-1] - print(f"::set-output name=version::{latest_version}") + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"version={latest_version}") - name: Update pinned version of jupyterhub if: steps.local.outputs.version != steps.latest.outputs.version From 89fbfcb68820e08ae248a3ba6d99da1d89c9e796 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 14 Nov 2022 15:07:22 +0100 Subject: [PATCH 593/898] ci: fix failure to emit a new line Co-authored-by: Simon Li --- .github/workflows/watch-dependencies.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 2b464ff9c6..201b379481 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -162,7 +162,7 @@ jobs: latest_version = sorted(releases.keys(), key=packaging.version.Version)[-1] with open(os.environ["GITHUB_OUTPUT"], "a") as f: - f.write(f"version={latest_version}") + f.write(f"version={latest_version}\n") - name: Update pinned version of jupyterhub if: steps.local.outputs.version != steps.latest.outputs.version From b814354ad0682a3c8e6f01859c2c3dc9d4215f26 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:45:36 +0000 Subject: [PATCH 594/898] Update pause version from 3.8 to 3.9 --- jupyterhub/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 08aefe7887..b58c0b5a53 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -537,7 +537,7 @@ scheduling: # # If you update this, also update prePuller.pause.image.tag # - tag: "3.8" + tag: "3.9" pullPolicy: pullSecrets: [] revisionHistoryLimit: @@ -623,7 +623,7 @@ prePuller: # # If you update this, also update scheduling.userPlaceholder.image.tag # - tag: "3.8" + tag: "3.9" pullPolicy: pullSecrets: [] From e7e4b30097d253bfc9898ce1c83c2a107b23ad8a Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 14 Nov 2022 21:46:05 +0000 Subject: [PATCH 595/898] hub image: refreeze requirements.txt --- images/singleuser-sample/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 6a99811e10..01d829904c 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -105,7 +105,7 @@ jupyter-core==5.0.0 # nbconvert # nbformat # notebook -jupyter-server==1.23.1 +jupyter-server==1.23.2 # via # jupyterlab # jupyterlab-server @@ -148,7 +148,7 @@ nbclassic==0.4.8 # retrolab nbclient==0.7.0 # via nbconvert -nbconvert==7.2.4 +nbconvert==7.2.5 # via # jupyter-server # nbclassic From b415559ad99c3d08f0ec43dd5fd85a837b4a9a96 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 18 Nov 2022 05:11:12 +0000 Subject: [PATCH 596/898] Update library/traefik version from v2.9.4 to v2.9.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index b58c0b5a53..113d674a20 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.4" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.5" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From ab5c6bf3093a1ce16d9c2647da08c970a19c0f2a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 21 Nov 2022 05:16:48 +0000 Subject: [PATCH 597/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index ccd66cc970..22ef3829e5 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-10-31_05:34:56 +# VULN_SCAN_TIME=2022-11-21_05:16:47 # The build stage From 855242da85f4b89b50dc9121e4a5641e2356029a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 21 Nov 2022 05:16:58 +0000 Subject: [PATCH 598/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 3e150a9963..f87f113cad 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-10-31_05:34:04 +# VULN_SCAN_TIME=2022-11-21_05:16:56 # The build stage From d11232b8ba808ef2586921b2ae8ffda8af4bcb63 Mon Sep 17 00:00:00 2001 From: arunppsg Date: Thu, 24 Nov 2022 13:26:35 +0530 Subject: [PATCH 599/898] [DOC] master node size update --- docs/source/kubernetes/amazon/step-zero-aws.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/amazon/step-zero-aws.md b/docs/source/kubernetes/amazon/step-zero-aws.md index f873c81b18..5b871108d8 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws.md +++ b/docs/source/kubernetes/amazon/step-zero-aws.md @@ -100,7 +100,7 @@ template you will use to setup and shape your cluster. kops create cluster $NAME \ --zones "$ZONES" \ --authorization RBAC \ - --master-size t2.micro \ + --master-size t2.medium \ --master-volume-size 10 \ --node-size t2.medium \ --node-volume-size 10 \ From cf31740c05079c1e222a96d449bd7e892e19b122 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 05:01:04 +0000 Subject: [PATCH 600/898] build(deps): bump peter-evans/create-pull-request from 4.1.3 to 4.2.2 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.1.3 to 4.2.2. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f...331d02c7e2104af23ad5974d4d5cbc58a3e6dc77) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 54a519e139..756b3d6677 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f + uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 201b379481..01b8a6bcf3 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f + uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -183,7 +183,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f + uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -216,7 +216,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@671dc9c9e0c2d73f07fa45a3eb0220e1622f0c5f + uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 9682dc10149e40a4cf1ca86ae8073b170284e75f Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 28 Nov 2022 05:37:32 +0000 Subject: [PATCH 601/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 10 +++++----- images/singleuser-sample/requirements.txt | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e80e1555d8..9ac694d781 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -34,7 +34,7 @@ charset-normalizer==2.1.1 # via # aiohttp # requests -cryptography==38.0.3 +cryptography==38.0.4 # via pyopenssl escapism==1.0.1 # via @@ -54,7 +54,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.17.0 +jsonschema==4.17.1 # via # jupyter-telemetry # oauthenticator @@ -90,7 +90,7 @@ kubernetes-asyncio==24.2.2 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.2.3 +mako==1.2.4 # via alembic markupsafe==2.1.1 # via @@ -147,7 +147,7 @@ python-dateutil==2.8.2 # kubernetes-asyncio python-json-logger==2.0.4 # via jupyter-telemetry -python-slugify==6.1.2 +python-slugify==7.0.0 # via jupyterhub-kubespawner pyyaml==6.0 # via @@ -192,7 +192,7 @@ traitlets==5.5.0 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.12 +urllib3==1.26.13 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 01d829904c..7df832004c 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -39,7 +39,7 @@ cffi==1.15.1 # cryptography charset-normalizer==2.1.1 # via requests -cryptography==38.0.3 +cryptography==38.0.4 # via pyopenssl debugpy==1.6.3 # via ipykernel @@ -71,7 +71,7 @@ ipython-genutils==0.2.0 # via # nbclassic # notebook -jedi==0.18.1 +jedi==0.18.2 # via ipython jinja2==3.1.2 # via @@ -84,12 +84,12 @@ jinja2==3.1.2 # notebook json5==0.9.10 # via jupyterlab-server -jsonschema==4.17.0 +jsonschema==4.17.1 # via # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.4.5 +jupyter-client==7.4.7 # via # ipykernel # jupyter-server @@ -105,7 +105,7 @@ jupyter-core==5.0.0 # nbconvert # nbformat # notebook -jupyter-server==1.23.2 +jupyter-server==1.23.3 # via # jupyterlab # jupyterlab-server @@ -127,7 +127,7 @@ jupyterlab-server==2.16.3 # via # jupyterlab # retrolab -mako==1.2.3 +mako==1.2.4 # via alembic markupsafe==2.1.1 # via @@ -203,7 +203,7 @@ prometheus-client==0.15.0 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.32 +prompt-toolkit==3.0.33 # via ipython psutil==5.9.4 # via ipykernel @@ -266,7 +266,7 @@ sqlalchemy==1.4.44 # via # alembic # jupyterhub -stack-data==0.6.1 +stack-data==0.6.2 # via ipython terminado==0.17.0 # via @@ -304,7 +304,7 @@ traitlets==5.5.0 # nbconvert # nbformat # notebook -urllib3==1.26.12 +urllib3==1.26.13 # via requests wcwidth==0.2.5 # via prompt-toolkit From 7d37b6ebfeb2c22d0f02d6a8a00f2f041843a88a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Nov 2022 01:56:31 +0000 Subject: [PATCH 602/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.2.0 → v3.2.2](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.2.2) - [github.com/PyCQA/flake8: 5.0.4 → 6.0.0](https://github.com/PyCQA/flake8/compare/5.0.4...6.0.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad4a49e7c3..6921db6b84 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.2.2 hooks: - id: pyupgrade args: @@ -65,7 +65,7 @@ repos: # Linting: Python code (see the file .flake8) - repo: https://github.com/PyCQA/flake8 - rev: "5.0.4" + rev: "6.0.0" hooks: - id: flake8 # Ignore style and complexity From 17ab6018a4eb6ff9247fa9afc730bbb6320e8616 Mon Sep 17 00:00:00 2001 From: arunppsg Date: Tue, 29 Nov 2022 11:12:17 +0530 Subject: [PATCH 603/898] updated instance type to use cheapest instance --- docs/source/kubernetes/amazon/step-zero-aws.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/kubernetes/amazon/step-zero-aws.md b/docs/source/kubernetes/amazon/step-zero-aws.md index 5b871108d8..18bd3599bd 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws.md +++ b/docs/source/kubernetes/amazon/step-zero-aws.md @@ -100,9 +100,9 @@ template you will use to setup and shape your cluster. kops create cluster $NAME \ --zones "$ZONES" \ --authorization RBAC \ - --master-size t2.medium \ + --master-size t3a.small \ --master-volume-size 10 \ - --node-size t2.medium \ + --node-size t3.medium \ --node-volume-size 10 \ --yes ``` From ffbf63eb6c4b30871c46b893597c760a9bb7a24b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 05:00:37 +0000 Subject: [PATCH 604/898] build(deps): bump peter-evans/create-pull-request from 4.2.2 to 4.2.3 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.2 to 4.2.3. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/331d02c7e2104af23ad5974d4d5cbc58a3e6dc77...2b011faafdcbc9ceb11414d64d0573f37c774b04) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 756b3d6677..947cc55eab 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 + uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 01b8a6bcf3..108bb7e433 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 + uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -183,7 +183,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 + uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -216,7 +216,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@331d02c7e2104af23ad5974d4d5cbc58a3e6dc77 + uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 065b613a93e899d03f37e56c98130a7a4c510cb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Dec 2022 05:00:39 +0000 Subject: [PATCH 605/898] build(deps): bump dessant/support-requests from 2 to 3 Bumps [dessant/support-requests](https://github.com/dessant/support-requests) from 2 to 3. - [Release notes](https://github.com/dessant/support-requests/releases) - [Changelog](https://github.com/dessant/support-requests/blob/master/CHANGELOG.md) - [Commits](https://github.com/dessant/support-requests/compare/v2...v3) --- updated-dependencies: - dependency-name: dessant/support-requests dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/support-bot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/support-bot.yml b/.github/workflows/support-bot.yml index 567e557436..571b829027 100644 --- a/.github/workflows/support-bot.yml +++ b/.github/workflows/support-bot.yml @@ -12,7 +12,7 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/support-requests@v2 + - uses: dessant/support-requests@v3 with: github-token: ${{ github.token }} support-label: "support" From 4469fa08890cdcb48207aede2fd876733d0356b2 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 5 Dec 2022 05:14:25 +0000 Subject: [PATCH 606/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 095eaf336e..e248f94199 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2022-10-10_06:01:28 +# VULN_SCAN_TIME=2022-12-05_05:14:23 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 617bec32e095b8e046eef03fd39e83c79e8ea40a Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 5 Dec 2022 05:21:39 +0000 Subject: [PATCH 607/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 12 ++++++------ images/singleuser-sample/requirements.txt | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 9ac694d781..4141e12310 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.11 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # @@ -54,7 +54,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.17.1 +jsonschema==4.17.3 # via # jupyter-telemetry # oauthenticator @@ -96,7 +96,7 @@ markupsafe==2.1.1 # via # jinja2 # mako -multidict==6.0.2 +multidict==6.0.3 # via # aiohttp # yarl @@ -187,7 +187,7 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.5.0 +traitlets==5.6.0 # via # jupyter-telemetry # jupyterhub @@ -197,7 +197,7 @@ urllib3==1.26.13 # jupyterhub-kubespawner # kubernetes-asyncio # requests -yarl==1.8.1 +yarl==1.8.2 # via aiohttp # The following packages are considered to be unsafe in a requirements file: diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 7df832004c..35414dc23c 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.11 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # @@ -15,7 +15,7 @@ argon2-cffi==21.3.0 # notebook argon2-cffi-bindings==21.2.0 # via argon2-cffi -asttokens==2.1.0 +asttokens==2.2.0 # via stack-data async-generator==1.10 # via jupyterhub @@ -41,7 +41,7 @@ charset-normalizer==2.1.1 # via requests cryptography==38.0.4 # via pyopenssl -debugpy==1.6.3 +debugpy==1.6.4 # via ipykernel decorator==5.1.1 # via ipython @@ -63,7 +63,7 @@ ipykernel==6.17.1 # via # nbclassic # notebook -ipython==8.6.0 +ipython==8.7.0 # via # ipykernel # jupyterlab @@ -84,7 +84,7 @@ jinja2==3.1.2 # notebook json5==0.9.10 # via jupyterlab-server -jsonschema==4.17.1 +jsonschema==4.17.3 # via # jupyter-telemetry # jupyterlab-server @@ -96,12 +96,13 @@ jupyter-client==7.4.7 # nbclassic # nbclient # notebook -jupyter-core==5.0.0 +jupyter-core==5.1.0 # via # jupyter-client # jupyter-server # jupyterlab # nbclassic + # nbclient # nbconvert # nbformat # notebook @@ -146,7 +147,7 @@ nbclassic==0.4.8 # jupyterlab # notebook # retrolab -nbclient==0.7.0 +nbclient==0.7.2 # via nbconvert nbconvert==7.2.5 # via @@ -167,7 +168,6 @@ nest-asyncio==1.5.6 # ipykernel # jupyter-client # nbclassic - # nbclient # notebook notebook==6.5.2 # via @@ -289,7 +289,7 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.5.0 +traitlets==5.6.0 # via # ipykernel # ipython From f0327067c41b9689e18777d522e318bffe4fe239 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 5 Dec 2022 14:39:25 +0000 Subject: [PATCH 608/898] Update jupyterhub from 3.0.0 to 3.1.0 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 10 +++++----- jupyterhub/Chart.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 47bc943d00..0263909d0b 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.0.0 +jupyterhub==3.1.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 4141e12310..5533afef30 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -60,7 +60,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.0.0 +jupyterhub==3.1.0 # via # -r requirements.in # jupyterhub-firstuseauthenticator diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 3a8249ea66..1a3db00027 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.0.0 +jupyterhub==3.1.0 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 35414dc23c..b8e1e92684 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -15,7 +15,7 @@ argon2-cffi==21.3.0 # notebook argon2-cffi-bindings==21.2.0 # via argon2-cffi -asttokens==2.2.0 +asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub @@ -89,7 +89,7 @@ jsonschema==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.4.7 +jupyter-client==7.4.8 # via # ipykernel # jupyter-server @@ -116,7 +116,7 @@ jupyter-server==1.23.3 # retrolab jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.0.0 +jupyterhub==3.1.0 # via -r requirements.in jupyterlab==3.4.8 # via @@ -149,7 +149,7 @@ nbclassic==0.4.8 # retrolab nbclient==0.7.2 # via nbconvert -nbconvert==7.2.5 +nbconvert==7.2.6 # via # jupyter-server # nbclassic @@ -268,7 +268,7 @@ sqlalchemy==1.4.44 # jupyterhub stack-data==0.6.2 # via ipython -terminado==0.17.0 +terminado==0.17.1 # via # jupyter-server # nbclassic diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 7e2a4489bf..64a4edadd5 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "3.0.0" +appVersion: "3.1.0" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 2b2a78ec7b1f624000815618bc3478d7987b9f7f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 00:32:41 +0000 Subject: [PATCH 609/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.2.2 → v3.3.0](https://github.com/asottile/pyupgrade/compare/v3.2.2...v3.3.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6921db6b84..2523d957d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 + rev: v3.3.0 hooks: - id: pyupgrade args: From 82b831b50b166644a307fd6fe6df633ccefae476 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 6 Dec 2022 05:10:14 +0000 Subject: [PATCH 610/898] Update jupyterhub/configurable-http-proxy version from 4.5.3 to 4.5.4 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 113d674a20..f65f8464ad 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -198,7 +198,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/tags + tag: "4.5.4" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From 762a6cd8be1730e4b6a2b4c22bc20c2c6559094a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 8 Dec 2022 05:10:05 +0000 Subject: [PATCH 611/898] Update library/traefik version from v2.9.5 to v2.9.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index f65f8464ad..138351becb 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.5" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.6" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 29dab5f2fd1ab26cc051aed65ef74037897d0f2a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Dec 2022 05:09:49 +0000 Subject: [PATCH 612/898] Update kube-scheduler version from v1.25.4 to v1.25.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 138351becb..40a6f170c6 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.25.4" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.5" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 7ed1cea72739ad22288fbc7f541be21b7be253e1 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Dec 2022 09:48:32 +0000 Subject: [PATCH 613/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 8 +++----- images/singleuser-sample/requirements.txt | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 5533afef30..59920f0069 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.9.24 +certifi==2022.12.7 # via # kubernetes-asyncio # requests @@ -114,7 +114,7 @@ oauthlib==3.2.2 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator -packaging==21.3 +packaging==22.0 # via jupyterhub pamela==1.0.0 # via jupyterhub @@ -136,8 +136,6 @@ pymysql==1.0.2 # via -r requirements.in pyopenssl==22.1.0 # via certipy -pyparsing==3.0.9 - # via packaging pyrsistent==0.19.2 # via jsonschema python-dateutil==2.8.2 @@ -187,7 +185,7 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.6.0 +traitlets==5.7.0 # via # jupyter-telemetry # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index b8e1e92684..c4e3dda719 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -29,7 +29,7 @@ beautifulsoup4==4.11.1 # via nbconvert bleach==5.0.1 # via nbconvert -certifi==2022.9.24 +certifi==2022.12.7 # via requests certipy==0.1.3 # via jupyterhub @@ -39,6 +39,8 @@ cffi==1.15.1 # cryptography charset-normalizer==2.1.1 # via requests +comm==0.1.2 + # via ipykernel cryptography==38.0.4 # via pyopenssl debugpy==1.6.4 @@ -59,7 +61,7 @@ idna==3.4 # via # anyio # requests -ipykernel==6.17.1 +ipykernel==6.19.2 # via # nbclassic # notebook @@ -124,7 +126,7 @@ jupyterlab==3.4.8 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.16.3 +jupyterlab-server==2.16.5 # via # jupyterlab # retrolab @@ -177,7 +179,7 @@ notebook-shim==0.2.2 # via nbclassic oauthlib==3.2.2 # via jupyterhub -packaging==21.3 +packaging==22.0 # via # ipykernel # jupyter-server @@ -195,7 +197,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==2.5.4 +platformdirs==2.6.0 # via jupyter-core prometheus-client==0.15.0 # via @@ -203,7 +205,7 @@ prometheus-client==0.15.0 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.33 +prompt-toolkit==3.0.36 # via ipython psutil==5.9.4 # via ipykernel @@ -221,8 +223,6 @@ pygments==2.13.0 # nbconvert pyopenssl==22.1.0 # via certipy -pyparsing==3.0.9 - # via packaging pyrsistent==0.19.2 # via jsonschema python-dateutil==2.8.2 @@ -289,8 +289,9 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.6.0 +traitlets==5.7.0 # via + # comm # ipykernel # ipython # jupyter-client From 3f7dcddd867552358521cd1127f4c2d2de98ebc9 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:13:56 +0000 Subject: [PATCH 614/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index e248f94199..4a9bccbbd2 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2022-12-05_05:14:23 +# VULN_SCAN_TIME=2022-12-12_05:13:54 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 2d087d6822552d0005a2602754714cae65a2da0c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Dec 2022 01:23:46 +0000 Subject: [PATCH 615/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.3.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.3.0...v3.3.1) - [github.com/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0) - [github.com/pycqa/isort: 5.10.1 → 5.11.1](https://github.com/pycqa/isort/compare/5.10.1...5.11.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2523d957d0..6bacca6431 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 + rev: v3.3.1 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black args: @@ -39,7 +39,7 @@ repos: # Autoformat: Python code - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.11.1 hooks: - id: isort args: From b45108ddabde7af845fd9d0d79d08f701a61cdde Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 19 Dec 2022 09:23:48 +0100 Subject: [PATCH 616/898] ci: fix vuln-scan regression following set-output deprecation --- .github/workflows/vuln-scan.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 947cc55eab..45450e9510 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -142,13 +142,13 @@ jobs: cat tmp/scan_$1.json | jq -r '.Results[] | select(.Vulnerabilities != null) | .Type + " | " + (.Vulnerabilities[] | .VulnerabilityID + " | " + .PkgName + " | " + .InstalledVersion + " | " + .FixedVersion)' | sort >> tmp/md_summary_$1.md fi - # Use hack to set a multiline string output - # ref: https://github.com/actions/toolkit/issues/403#issue-593398879 - TMP=$(cat tmp/md_summary_$1.md) - TMP="${TMP//'%'/'%25'}" - TMP="${TMP//$'\n'/'%0A'}" - TMP="${TMP//$'\r'/'%0D'}" - echo "md_summary_$1=$TMP" >> $GITHUB_OUTPUT + # Set a multiline string output with the following technique: + # ref: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings + # + eof_marker=EOF_$RANDOM + echo "md_summary_$1<<$eof_marker" >> $GITHUB_OUTPUT + cat tmp/md_summary_$1.md >> $GITHUB_OUTPUT + echo "$eof_marker" >> $GITHUB_OUTPUT # Calculate a hash of the markdown summary HASH=$(cat tmp/md_summary_$1.md | sha1sum) From 65c52001881fc1211aec731859158f581abdd816 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 19 Dec 2022 08:36:01 +0000 Subject: [PATCH 617/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index f87f113cad..623e0c1413 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-11-21_05:16:56 +# VULN_SCAN_TIME=2022-12-19_08:35:59 # The build stage From a00edc26f6b623f7954a7439494a8cfb6d973fc1 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 19 Dec 2022 08:37:14 +0000 Subject: [PATCH 618/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 22ef3829e5..e96afa3208 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-11-21_05:16:47 +# VULN_SCAN_TIME=2022-12-19_08:37:12 # The build stage From f7c9802d708a98d9688521a1c3d195e031a79a29 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 01:21:03 +0000 Subject: [PATCH 619/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: 5.11.1 → v5.11.3](https://github.com/pycqa/isort/compare/5.11.1...v5.11.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6bacca6431..defbe97d3e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,7 +39,7 @@ repos: # Autoformat: Python code - repo: https://github.com/pycqa/isort - rev: 5.11.1 + rev: v5.11.3 hooks: - id: isort args: From 371846e36521c685c4fab6de84370e42f478b378 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 20 Dec 2022 10:05:01 +0100 Subject: [PATCH 620/898] pre-commit.ci: update monthly --- .pre-commit-config.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index defbe97d3e..f811b31ba8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,7 @@ repos: - --target-version=py38 - --target-version=py39 - --target-version=py310 + - --target-version=py311 # Autoformat: Python code - repo: https://github.com/pycqa/isort @@ -76,3 +77,7 @@ repos: args: - --ignore=E,C,W - --builtins=c + +# pre-commit.ci config reference: https://pre-commit.ci/#configuration +ci: + autoupdate_schedule: monthly From 33898127abf52835bbdd490cd890a186c3919b18 Mon Sep 17 00:00:00 2001 From: Aaron Newman <63217782+aaronjnewman@users.noreply.github.com> Date: Thu, 22 Dec 2022 23:14:07 -0400 Subject: [PATCH 621/898] note at line 554 did not render correctly --- docs/source/jupyterhub/customizing/user-environment.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 524826d63a..8f7a5f09cb 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -551,12 +551,12 @@ singleuser: - "--other-flag" ``` -:::{note} +```{note} Docker has `ENTRYPOINT` and `CMD`, which k8s calls `command` and `args`. zero-to-jupyterhub always respects the ENTRYPOINT of the image, and setting `singleuser.cmd` only overrides the CMD. -::: +``` ## Disable specific JupyterLab extensions From 221a70b734bbeeb8f5ccfe1922c2b0ce036d8d73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jan 2023 05:00:48 +0000 Subject: [PATCH 622/898] build(deps): bump jupyterhub/action-k8s-await-workloads from 1 to 2 Bumps [jupyterhub/action-k8s-await-workloads](https://github.com/jupyterhub/action-k8s-await-workloads) from 1 to 2. - [Release notes](https://github.com/jupyterhub/action-k8s-await-workloads/releases) - [Changelog](https://github.com/jupyterhub/action-k8s-await-workloads/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyterhub/action-k8s-await-workloads/compare/v1...v2) --- updated-dependencies: - dependency-name: jupyterhub/action-k8s-await-workloads dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test-chart.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index e39cb3817b..391e75833e 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -267,7 +267,7 @@ jobs: # jupyterhub and the autohttps pod is about to start, so for CI # performance we delayed this until now and did other things in between. - name: Await local ACME server - uses: jupyterhub/action-k8s-await-workloads@v1 + uses: jupyterhub/action-k8s-await-workloads@v2 with: timeout: 150 max-restarts: 1 @@ -331,7 +331,7 @@ jobs: - name: "(Upgrade) Await ${{ matrix.upgrade-from }} chart" if: matrix.test == 'upgrade' - uses: jupyterhub/action-k8s-await-workloads@v1 + uses: jupyterhub/action-k8s-await-workloads@v2 with: timeout: 150 max-restarts: 1 @@ -356,7 +356,7 @@ jobs: ${{ matrix.local-chart-extra-args }} - name: "Await local chart" - uses: jupyterhub/action-k8s-await-workloads@v1 + uses: jupyterhub/action-k8s-await-workloads@v2 with: timeout: 150 max-restarts: 1 From 8bc90158f6064862eeb8076903a55a0702b5d001 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 01:51:59 +0000 Subject: [PATCH 623/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/isort: v5.11.3 → 5.11.4](https://github.com/pycqa/isort/compare/v5.11.3...5.11.4) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f811b31ba8..6a50120b39 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,7 +40,7 @@ repos: # Autoformat: Python code - repo: https://github.com/pycqa/isort - rev: v5.11.3 + rev: 5.11.4 hooks: - id: isort args: From 04c8d08254cd5df78ec7d3de2a7ba66275530b8d Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 9 Jan 2023 05:13:32 +0000 Subject: [PATCH 624/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 4a9bccbbd2..d5d7844946 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2022-12-12_05:13:54 +# VULN_SCAN_TIME=2023-01-09_05:13:30 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 5021fb6f966ceacd9ff15d1622615a9d68219949 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 9 Jan 2023 05:14:45 +0000 Subject: [PATCH 625/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 623e0c1413..e949488d12 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-12-19_08:35:59 +# VULN_SCAN_TIME=2023-01-09_05:14:43 # The build stage From 121615c1da23d50e1cf28093ad85113026ced10c Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 9 Jan 2023 05:14:50 +0000 Subject: [PATCH 626/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index e96afa3208..57c4353e32 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2022-12-19_08:37:12 +# VULN_SCAN_TIME=2023-01-09_05:14:49 # The build stage From 7db949eb116536b78862443982caafcd2a018ffa Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 16 Jan 2023 05:13:45 +0000 Subject: [PATCH 627/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index d5d7844946..1f03180e8b 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-01-09_05:13:30 +# VULN_SCAN_TIME=2023-01-16_05:13:44 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 257b05efbf5d630a4126b02ddd4967bd5c77b6a0 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 19 Jan 2023 05:09:49 +0000 Subject: [PATCH 628/898] Update kube-scheduler version from v1.25.5 to v1.25.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 40a6f170c6..7d9e509fa0 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.25.5" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.6" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 00fb960500b9a29f540c4147b421b67fb265f17d Mon Sep 17 00:00:00 2001 From: Pris Nasrat Date: Thu, 26 Jan 2023 08:26:38 -0500 Subject: [PATCH 629/898] Documentation fix for running k8s-singleuser-sample locally Local docker run needs to bind externally for expose to work. Remove outdated instructions running on old/new interface. Reported via https://discourse.jupyter.org/t/how-to-test-k8s-singleuser-sample-alone-and-locally-without-deploying-to-eks/17694/1 --- images/singleuser-sample/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/images/singleuser-sample/README.md b/images/singleuser-sample/README.md index ce87c71412..d3b03cb07d 100644 --- a/images/singleuser-sample/README.md +++ b/images/singleuser-sample/README.md @@ -16,11 +16,8 @@ guide's summary about container technology.](https://z2jh.jupyter.org/en/latest/ To quickly try out this Docker image on your computer: ```sh -# with the classic UI -docker run -it --rm -p 8888:8888 jupyterhub/k8s-singleuser-sample:0.7.0 - # with JupyterLab -docker run -it --rm -p 8888:8888 -e JUPYTER_ENABLE_LAB=true jupyterhub/k8s-singleuser-sample:0.7.0 +docker run -it --rm -p 8888:8888 jupyterhub/k8s-singleuser-sample:2.0.0 -- jupyter lab --ip 0.0.0.0 ``` This image available tags can be found [here](https://hub.docker.com/r/jupyterhub/k8s-singleuser-sample/tags/). From 7ea0fcf4a8b6e0256684d0da1a5993e2e33f236e Mon Sep 17 00:00:00 2001 From: Pris Nasrat Date: Thu, 26 Jan 2023 07:37:07 -0500 Subject: [PATCH 630/898] Update custom image docs to reflect multistage build --- docs/source/administrator/services.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index 1d77430dae..1a4dd7553e 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -18,11 +18,15 @@ In the following snippet, I'm using a custom image that copies over the applicat ```Dockerfile # Dockerfile -# 0.11.1 is latest stable release at the time of this writing -FROM jupyterhub/k8s-hub:0.11.1 +# 2.0.0 is latest stable release at the time of this writing +FROM jupyterhub/k8s-hub:2.0.0 +# The k8s-hub uses a multi-stage build to modify packages your build steps may need to run as root +USER root COPY ./service-fastapi /usr/src/fastapi RUN python3 -m pip install -r /usr/src/fastapi/requirements.txt + +USER ${NB_USER} ``` ```yaml From 877abcecfb1986059665156940cae56417f23c71 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 28 Jan 2023 05:09:22 +0000 Subject: [PATCH 631/898] Update jupyterhub from 3.1.0 to 3.1.1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 32 ++++++----- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 69 +++++++++++------------ jupyterhub/Chart.yaml | 2 +- 5 files changed, 54 insertions(+), 53 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index 0263909d0b..d5a788ccec 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.1.0 +jupyterhub==3.1.1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 59920f0069..b677de6263 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,13 +8,13 @@ aiohttp==3.8.3 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.8.1 +alembic==1.9.2 # via jupyterhub async-generator==1.10 # via jupyterhub async-timeout==4.0.2 # via aiohttp -attrs==22.1.0 +attrs==22.2.0 # via # aiohttp # jsonschema @@ -34,7 +34,7 @@ charset-normalizer==2.1.1 # via # aiohttp # requests -cryptography==38.0.4 +cryptography==39.0.0 # via pyopenssl escapism==1.0.1 # via @@ -60,7 +60,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.1.0 +jupyterhub==3.1.1 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -92,11 +92,11 @@ ldap3==2.9.1 # via jupyterhub-ldapauthenticator mako==1.2.4 # via alembic -markupsafe==2.1.1 +markupsafe==2.1.2 # via # jinja2 # mako -multidict==6.0.3 +multidict==6.0.4 # via # aiohttp # yarl @@ -114,11 +114,11 @@ oauthlib==3.2.2 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator -packaging==22.0 +packaging==23.0 # via jupyterhub pamela==1.0.0 # via jupyterhub -prometheus-client==0.15.0 +prometheus-client==0.16.0 # via jupyterhub psycopg2-binary==2.9.5 # via -r requirements.in @@ -126,7 +126,7 @@ pyasn1==0.4.8 # via ldap3 pycparser==2.21 # via cffi -pycurl==7.45.1 +pycurl==7.45.2 # via -r requirements.in pyjwt==2.6.0 # via @@ -134,9 +134,9 @@ pyjwt==2.6.0 # mwoauth pymysql==1.0.2 # via -r requirements.in -pyopenssl==22.1.0 +pyopenssl==23.0.0 # via certipy -pyrsistent==0.19.2 +pyrsistent==0.19.3 # via jsonschema python-dateutil==2.8.2 # via @@ -151,7 +151,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.28.1 +requests==2.28.2 # via # jupyterhub # mwoauth @@ -169,7 +169,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==1.4.44 +sqlalchemy==2.0.0 # via # alembic # jupyterhub @@ -185,12 +185,14 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.7.0 +traitlets==5.8.1 # via # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -urllib3==1.26.13 +typing-extensions==4.4.0 + # via sqlalchemy +urllib3==1.26.14 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 1a3db00027..785e00e368 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.1.0 +jupyterhub==3.1.1 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index c4e3dda719..4d9cba1de0 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.8.1 +alembic==1.9.2 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -19,7 +19,7 @@ asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub -attrs==22.1.0 +attrs==22.2.0 # via jsonschema babel==2.11.0 # via jupyterlab-server @@ -27,7 +27,7 @@ backcall==0.2.0 # via ipython beautifulsoup4==4.11.1 # via nbconvert -bleach==5.0.1 +bleach==6.0.0 # via nbconvert certifi==2022.12.7 # via requests @@ -37,20 +37,18 @@ cffi==1.15.1 # via # argon2-cffi-bindings # cryptography -charset-normalizer==2.1.1 +charset-normalizer==3.0.1 # via requests comm==0.1.2 # via ipykernel -cryptography==38.0.4 +cryptography==39.0.0 # via pyopenssl -debugpy==1.6.4 +debugpy==1.6.6 # via ipykernel decorator==5.1.1 # via ipython defusedxml==0.7.1 # via nbconvert -entrypoints==0.4 - # via jupyter-client executing==1.2.0 # via stack-data fastjsonschema==2.16.2 @@ -61,11 +59,11 @@ idna==3.4 # via # anyio # requests -ipykernel==6.19.2 +ipykernel==6.20.2 # via # nbclassic # notebook -ipython==8.7.0 +ipython==8.9.0 # via # ipykernel # jupyterlab @@ -84,21 +82,21 @@ jinja2==3.1.2 # nbclassic # nbconvert # notebook -json5==0.9.10 +json5==0.9.11 # via jupyterlab-server jsonschema==4.17.3 # via # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==7.4.8 +jupyter-client==8.0.1 # via # ipykernel # jupyter-server # nbclassic # nbclient # notebook -jupyter-core==5.1.0 +jupyter-core==5.1.5 # via # jupyter-client # jupyter-server @@ -108,7 +106,7 @@ jupyter-core==5.1.0 # nbconvert # nbformat # notebook -jupyter-server==1.23.3 +jupyter-server==1.23.5 # via # jupyterlab # jupyterlab-server @@ -118,7 +116,7 @@ jupyter-server==1.23.3 # retrolab jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.1.0 +jupyterhub==3.1.1 # via -r requirements.in jupyterlab==3.4.8 # via @@ -126,13 +124,13 @@ jupyterlab==3.4.8 # retrolab jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.16.5 +jupyterlab-server==2.19.0 # via # jupyterlab # retrolab mako==1.2.4 # via alembic -markupsafe==2.1.1 +markupsafe==2.1.2 # via # jinja2 # mako @@ -143,7 +141,7 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.4 # via nbconvert -nbclassic==0.4.8 +nbclassic==0.5.1 # via # -r requirements.in # jupyterlab @@ -151,12 +149,12 @@ nbclassic==0.4.8 # retrolab nbclient==0.7.2 # via nbconvert -nbconvert==7.2.6 +nbconvert==7.2.9 # via # jupyter-server # nbclassic # notebook -nbformat==5.7.0 +nbformat==5.7.3 # via # jupyter-server # nbclassic @@ -168,7 +166,6 @@ nbgitpuller==1.1.1 nest-asyncio==1.5.6 # via # ipykernel - # jupyter-client # nbclassic # notebook notebook==6.5.2 @@ -179,7 +176,7 @@ notebook-shim==0.2.2 # via nbclassic oauthlib==3.2.2 # via jupyterhub -packaging==22.0 +packaging==23.0 # via # ipykernel # jupyter-server @@ -197,9 +194,9 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==2.6.0 +platformdirs==2.6.2 # via jupyter-core -prometheus-client==0.15.0 +prometheus-client==0.16.0 # via # jupyter-server # jupyterhub @@ -217,13 +214,13 @@ pure-eval==0.2.2 # via stack-data pycparser==2.21 # via cffi -pygments==2.13.0 +pygments==2.14.0 # via # ipython # nbconvert -pyopenssl==22.1.0 +pyopenssl==23.0.0 # via certipy -pyrsistent==0.19.2 +pyrsistent==0.19.3 # via jsonschema python-dateutil==2.8.2 # via @@ -231,16 +228,16 @@ python-dateutil==2.8.2 # jupyterhub python-json-logger==2.0.4 # via jupyter-telemetry -pytz==2022.6 +pytz==2022.7.1 # via babel -pyzmq==24.0.1 +pyzmq==25.0.0 # via # ipykernel # jupyter-client # jupyter-server # nbclassic # notebook -requests==2.28.1 +requests==2.28.2 # via # jupyterhub # jupyterlab-server @@ -262,7 +259,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==1.4.44 +sqlalchemy==2.0.0 # via # alembic # jupyterhub @@ -289,7 +286,7 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.7.0 +traitlets==5.8.1 # via # comm # ipykernel @@ -305,13 +302,15 @@ traitlets==5.7.0 # nbconvert # nbformat # notebook -urllib3==1.26.13 +typing-extensions==4.4.0 + # via sqlalchemy +urllib3==1.26.14 # via requests -wcwidth==0.2.5 +wcwidth==0.2.6 # via prompt-toolkit webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.4.2 +websocket-client==1.5.0 # via jupyter-server diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 64a4edadd5..837a626375 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "3.1.0" +appVersion: "3.1.1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 78a292174f4101a366452b2d2f5db79eb253cb07 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 30 Jan 2023 05:14:29 +0000 Subject: [PATCH 632/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index e949488d12..e6abcb56b6 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-01-09_05:14:43 +# VULN_SCAN_TIME=2023-01-30_05:14:27 # The build stage From 311b4968704d50aab9dc74bb6dede7fa4144829b Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 30 Jan 2023 05:14:57 +0000 Subject: [PATCH 633/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 57c4353e32..f8805ed837 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-01-09_05:14:49 +# VULN_SCAN_TIME=2023-01-30_05:14:55 # The build stage From 9c250e89302badc670668f09cc0a233bc42c2d0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 05:00:46 +0000 Subject: [PATCH 634/898] build(deps): bump aquasecurity/trivy-action from 0.8.0 to 0.9.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/9ab158e8597f3b310480b9a69402b419bc03dbd5...cff3e9a7f62c41dd51975266d0ae235709e39c41) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 45450e9510..dd1bb99096 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 + uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 + uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@9ab158e8597f3b310480b9a69402b419bc03dbd5 + uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 with: image-ref: rebuilt-image format: table From a14939562e4da50268bc0f11ec5afb086bb41f4d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 04:39:00 +0000 Subject: [PATCH 635/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 22.12.0 → 23.1.0](https://github.com/psf/black/compare/22.12.0...23.1.0) - [github.com/pycqa/isort: 5.11.4 → 5.12.0](https://github.com/pycqa/isort/compare/5.11.4...5.12.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6a50120b39..494ed95fce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black args: @@ -40,7 +40,7 @@ repos: # Autoformat: Python code - repo: https://github.com/pycqa/isort - rev: 5.11.4 + rev: 5.12.0 hooks: - id: isort args: From c7643fbb0e51b6e45ea1db7a53d624d2e887aa90 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 7 Feb 2023 09:07:14 +0100 Subject: [PATCH 636/898] ci: fix for redirect to hub.jupyter.org --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 391e75833e..666ce5266f 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -284,7 +284,7 @@ jobs: run: | . ./ci/common if [ ${{ matrix.upgrade-from }} = stable -o ${{ matrix.upgrade-from }} = dev ]; then - UPGRADE_FROM_VERSION=$(curl -sS https://jupyterhub.github.io/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') + UPGRADE_FROM_VERSION=$(curl -sSL https://jupyterhub.github.io/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') else UPGRADE_FROM_VERSION=${{ matrix.upgrade-from }} fi From df257a2cc6bc49ab7a8c22e8cdda2d96ae38dd39 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 8 Feb 2023 05:49:32 +0000 Subject: [PATCH 637/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 12 +++++------ images/singleuser-sample/requirements.txt | 25 ++++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index b677de6263..47740c6ecc 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.3 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.9.2 +alembic==1.9.3 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -34,7 +34,7 @@ charset-normalizer==2.1.1 # via # aiohttp # requests -cryptography==39.0.0 +cryptography==39.0.1 # via pyopenssl escapism==1.0.1 # via @@ -44,7 +44,7 @@ frozenlist==1.3.3 # via # aiohttp # aiosignal -greenlet==2.0.1 +greenlet==2.0.2 # via sqlalchemy idna==3.4 # via @@ -145,7 +145,7 @@ python-dateutil==2.8.2 # kubernetes-asyncio python-json-logger==2.0.4 # via jupyter-telemetry -python-slugify==7.0.0 +python-slugify==8.0.0 # via jupyterhub-kubespawner pyyaml==6.0 # via @@ -169,7 +169,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.0 +sqlalchemy==2.0.2 # via # alembic # jupyterhub @@ -185,7 +185,7 @@ tornado==6.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator -traitlets==5.8.1 +traitlets==5.9.0 # via # jupyter-telemetry # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 4d9cba1de0..164b0ca572 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.9.2 +alembic==1.9.3 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -25,7 +25,7 @@ babel==2.11.0 # via jupyterlab-server backcall==0.2.0 # via ipython -beautifulsoup4==4.11.1 +beautifulsoup4==4.11.2 # via nbconvert bleach==6.0.0 # via nbconvert @@ -41,7 +41,7 @@ charset-normalizer==3.0.1 # via requests comm==0.1.2 # via ipykernel -cryptography==39.0.0 +cryptography==39.0.1 # via pyopenssl debugpy==1.6.6 # via ipykernel @@ -53,13 +53,13 @@ executing==1.2.0 # via stack-data fastjsonschema==2.16.2 # via nbformat -greenlet==2.0.1 +greenlet==2.0.2 # via sqlalchemy idna==3.4 # via # anyio # requests -ipykernel==6.20.2 +ipykernel==6.21.1 # via # nbclassic # notebook @@ -89,15 +89,16 @@ jsonschema==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==8.0.1 +jupyter-client==8.0.2 # via # ipykernel # jupyter-server # nbclassic # nbclient # notebook -jupyter-core==5.1.5 +jupyter-core==5.2.0 # via + # ipykernel # jupyter-client # jupyter-server # jupyterlab @@ -139,7 +140,7 @@ matplotlib-inline==0.1.6 # via # ipykernel # ipython -mistune==2.0.4 +mistune==2.0.5 # via nbconvert nbclassic==0.5.1 # via @@ -194,7 +195,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==2.6.2 +platformdirs==3.0.0 # via jupyter-core prometheus-client==0.16.0 # via @@ -259,7 +260,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==2.0.0 +sqlalchemy==2.0.2 # via # alembic # jupyterhub @@ -286,7 +287,7 @@ tornado==6.2 # notebook # retrolab # terminado -traitlets==5.8.1 +traitlets==5.9.0 # via # comm # ipykernel @@ -312,5 +313,5 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.0 +websocket-client==1.5.1 # via jupyter-server From 2801ca71fd0081b0c59da2e3f64893405a8be81a Mon Sep 17 00:00:00 2001 From: Pan Luo Date: Wed, 8 Feb 2023 15:21:14 -0800 Subject: [PATCH 638/898] Fix broken link --- docs/source/jupyterhub/customizing/user-management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-management.md b/docs/source/jupyterhub/customizing/user-management.md index 468bbc5b9d..a79450f238 100644 --- a/docs/source/jupyterhub/customizing/user-management.md +++ b/docs/source/jupyterhub/customizing/user-management.md @@ -84,7 +84,7 @@ are being used as expected. ## Admin Users JupyterHub has the concept of -[admin users](https://jupyterhub.readthedocs.io/en/latest/getting-started/authenticators-users-basics.html#configure-admins-admin-users) +[admin users](https://jupyterhub.readthedocs.io/en/stable/getting-started/authenticators-users-basics.html#configure-admins-admin-users) who have special rights. They can start / stop other user's servers, and optionally access user's notebooks. They will see a new **Admin** button in their Control Panel which will take them to an **Admin Panel** where they can From 3053d41b126c277f26dda391b17e7dbbb0a24dc1 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:13:46 +0000 Subject: [PATCH 639/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index aa47f065bc..3be481e544 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2022-08-15_05:35:25 +# VULN_SCAN_TIME=2023-02-13_05:13:45 RUN apk add --no-cache iptables From 2ae7e0328595021557c3f75a7b66f16c1cb79f90 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:13:59 +0000 Subject: [PATCH 640/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 1f03180e8b..15a414a2de 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-01-16_05:13:44 +# VULN_SCAN_TIME=2023-02-13_05:13:57 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 3f4b8600b52a4e24f3d0ae3b3e2247e530257ac8 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:15:00 +0000 Subject: [PATCH 641/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index f8805ed837..e1d4503dc6 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-01-30_05:14:55 +# VULN_SCAN_TIME=2023-02-13_05:14:59 # The build stage From 9482d9b04b370cc9fa6e3523ae04e8f738624a5c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 05:56:40 +0000 Subject: [PATCH 642/898] build(deps): bump aquasecurity/trivy-action from 0.9.0 to 0.9.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.9.0 to 0.9.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/cff3e9a7f62c41dd51975266d0ae235709e39c41...8bd2f9fbda2109502356ff8a6a89da55b1ead252) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index dd1bb99096..0bcb4ae38d 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 + uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 + uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@cff3e9a7f62c41dd51975266d0ae235709e39c41 + uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 with: image-ref: rebuilt-image format: table From a7d20f9cf48f3b12525cf8dd2403239bfe2c6106 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 13 Feb 2023 06:21:10 +0000 Subject: [PATCH 643/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 8 ++++---- images/singleuser-sample/requirements.txt | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 47740c6ecc..049c6b92a3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.8.3 +aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp @@ -30,7 +30,7 @@ certipy==0.1.3 # via jupyterhub cffi==1.15.1 # via cryptography -charset-normalizer==2.1.1 +charset-normalizer==3.0.1 # via # aiohttp # requests @@ -143,7 +143,7 @@ python-dateutil==2.8.2 # jupyterhub # jupyterhub-idle-culler # kubernetes-asyncio -python-json-logger==2.0.4 +python-json-logger==2.0.5 # via jupyter-telemetry python-slugify==8.0.0 # via jupyterhub-kubespawner @@ -169,7 +169,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.2 +sqlalchemy==2.0.3 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 164b0ca572..97ae99a106 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -63,7 +63,7 @@ ipykernel==6.21.1 # via # nbclassic # notebook -ipython==8.9.0 +ipython==8.10.0 # via # ipykernel # jupyterlab @@ -227,7 +227,7 @@ python-dateutil==2.8.2 # via # jupyter-client # jupyterhub -python-json-logger==2.0.4 +python-json-logger==2.0.5 # via jupyter-telemetry pytz==2022.7.1 # via babel @@ -260,7 +260,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.3.2.post1 # via beautifulsoup4 -sqlalchemy==2.0.2 +sqlalchemy==2.0.3 # via # alembic # jupyterhub From 3f4e717f6ca062333e5b41be5b82ddfd33388702 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 15 Feb 2023 05:10:35 +0000 Subject: [PATCH 644/898] Update library/traefik version from v2.9.6 to v2.9.7 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7d9e509fa0..e2ab681380 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.6" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.7" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 458d566cb568b91e38a9a95cfa23eab96dc7f495 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 16 Feb 2023 05:10:09 +0000 Subject: [PATCH 645/898] Update library/traefik version from v2.9.7 to v2.9.8 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index e2ab681380..df5573be16 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.7" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.8" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 0b7b900a29af16c9034aff37d4ce57e446f2b325 Mon Sep 17 00:00:00 2001 From: HoseonRyu Date: Fri, 17 Feb 2023 15:48:38 +0900 Subject: [PATCH 646/898] Fix existing secret --- jupyterhub/templates/_helpers-names.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index bbb5864e28..740e0544f7 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -90,7 +90,9 @@ {{- /* A hack to avoid issues from invoking this from a parent Helm chart. */}} {{- $existing_secret := .Values.hub.existingSecret }} {{- if ne .Chart.Name "jupyterhub" }} - {{- $existing_secret = .Values.jupyterhub.hub.existingSecret }} + {{- if .Values.jupyterhub }} + {{- $existing_secret = .Values.jupyterhub.hub.existingSecret }} + {{- end }} {{- end }} {{- if $existing_secret }} {{- $existing_secret }} From c93be60141969fbddb09d65f00d7bb1cbb643807 Mon Sep 17 00:00:00 2001 From: HoseonRyu Date: Fri, 17 Feb 2023 16:00:27 +0900 Subject: [PATCH 647/898] Add appLable to proxy service --- jupyterhub/templates/proxy/service.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/templates/proxy/service.yaml b/jupyterhub/templates/proxy/service.yaml index 8a96eb135c..3279f3bce0 100644 --- a/jupyterhub/templates/proxy/service.yaml +++ b/jupyterhub/templates/proxy/service.yaml @@ -41,6 +41,7 @@ spec: component: proxy {{- end }} release: {{ .Release.Name }} + app: {{ .appLabel | default (include "jupyterhub.appLabel" .) }} ports: {{- if $HTTPS }} - name: https From 1711480a4600b24d2d94000e540a4c73c85dc6b2 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 20 Feb 2023 05:15:16 +0000 Subject: [PATCH 648/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index e6abcb56b6..e0b8c77558 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-01-30_05:14:27 +# VULN_SCAN_TIME=2023-02-20_05:15:14 # The build stage From b2101100a9f31ceb53ae8c1f3a01c623278052c3 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 20 Feb 2023 05:15:25 +0000 Subject: [PATCH 649/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index e1d4503dc6..38580443bc 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-02-13_05:14:59 +# VULN_SCAN_TIME=2023-02-20_05:15:23 # The build stage From 237027b9496832d4c5c2ac863b334b65fd22c36c Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 23 Feb 2023 05:10:14 +0000 Subject: [PATCH 650/898] Update kube-scheduler version from v1.25.6 to v1.25.7 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index df5573be16..8acb9aa36a 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.25.6" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.7" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 5696e96abae8944526742d0b4c0f922500a309d1 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Feb 2023 05:15:27 +0000 Subject: [PATCH 651/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index e0b8c77558..8d6876b376 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-02-20_05:15:14 +# VULN_SCAN_TIME=2023-02-27_05:15:25 # The build stage From 45c843f5ca77bd4530181756433a2a8f74f17296 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Feb 2023 05:15:41 +0000 Subject: [PATCH 652/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 38580443bc..420e4f9a22 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-02-20_05:15:23 +# VULN_SCAN_TIME=2023-02-27_05:15:39 # The build stage From 5b9f7acda8c0b17ff73b4b5ea826259b847a78c1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Feb 2023 09:17:24 +0100 Subject: [PATCH 653/898] Fix implementation of proxy-public Service's selector --- jupyterhub/templates/proxy/service.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jupyterhub/templates/proxy/service.yaml b/jupyterhub/templates/proxy/service.yaml index 3279f3bce0..13321a0265 100644 --- a/jupyterhub/templates/proxy/service.yaml +++ b/jupyterhub/templates/proxy/service.yaml @@ -35,13 +35,15 @@ metadata: {{- end }} spec: selector: + # This service will target the autohttps pod if autohttps is configured, and + # the proxy pod if not. When autohttps is configured, the service proxy-http + # will be around to target the proxy pod directly. {{- if $autoHTTPS }} - component: autohttps + {{- $_ := merge (dict "componentLabel" "autohttps") . -}} + {{- include "jupyterhub.matchLabels" $_ | nindent 4 }} {{- else }} - component: proxy + {{- include "jupyterhub.matchLabels" . | nindent 4 }} {{- end }} - release: {{ .Release.Name }} - app: {{ .appLabel | default (include "jupyterhub.appLabel" .) }} ports: {{- if $HTTPS }} - name: https From 9d2e3bf7c17e3912eb76f5de457a8773dd7cdebc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Feb 2023 09:44:31 +0100 Subject: [PATCH 654/898] Drop support for k8s 1.21 --- .github/workflows/test-chart.yaml | 6 +++--- jupyterhub/Chart.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 666ce5266f..2304322e5d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -135,12 +135,12 @@ jobs: test: install local-chart-extra-args: >- --set hub.image.name=jupyterhub/k8s-hub-slim - - k3s-channel: v1.22 # also test prePuller.hook + - k3s-channel: v1.26 # also test prePuller.hook test: install local-chart-extra-args: >- --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - - k3s-channel: v1.21 # also test hub.existingSecret + - k3s-channel: v1.25 # also test hub.existingSecret test: install local-chart-extra-args: >- --set hub.existingSecret=test-hub-existing-secret @@ -163,7 +163,7 @@ jobs: # information from # https://jupyterhub.github.io/helm-chart/info.json # - - k3s-channel: v1.23 + - k3s-channel: v1.24 test: upgrade upgrade-from: stable upgrade-from-extra-args: >- diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 837a626375..eed73756b5 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -8,7 +8,7 @@ keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg -kubeVersion: ">=1.21.0-0" +kubeVersion: ">=1.22.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers # listed, we have added some below, but in practice the entire JupyterHub team From 61e18242138cf4e716495117e4f1dfadca84abdc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Feb 2023 09:44:56 +0100 Subject: [PATCH 655/898] docs: update notes about user-schedulers RBAC resources --- jupyterhub/templates/scheduling/user-scheduler/rbac.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 01bfc78ba2..4acabd8b81 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -19,9 +19,9 @@ rules: # - unchanged between 1.18 and 1.20 # - changed in 1.21: get/list/watch permission for namespace, # csidrivers, csistoragecapacities was added. - # - unchanged between 1.22 and 1.25 + # - unchanged between 1.22 and 1.26 # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.25.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L730-L886 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.26.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L730-L886 - apiGroups: - "" - events.k8s.io @@ -183,9 +183,9 @@ rules: # Copied from the system:volume-scheduler ClusterRole of the k8s version # matching the kube-scheduler binary we use. # - # NOTE: These rules have not changed between 1.12 and 1.25. + # NOTE: These rules have not changed between 1.12 and 1.26. # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.25.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1305-L1332 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.26.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1306-L1333 - apiGroups: - "" resources: From 0e7347d714206a2316d49c0470973424d048ebc4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Feb 2023 09:49:19 +0100 Subject: [PATCH 656/898] docs: update comments in Dockerfiles to align with binderhub --- images/hub/Dockerfile | 8 ++++++-- images/singleuser-sample/Dockerfile | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 8d6876b376..61402f52bf 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -7,12 +7,16 @@ # This stage is building Python wheels for use in later stages by using a base # image that has more pre-requisites to do so, such as a C++ compiler. # +# NOTE: If the image version is updated, also update it in ci/refreeze and +# singleuser-sample's Dockerfile! +# FROM python:3.11-bullseye as build-stage # Build wheels # # We set pip's cache directory and expose it across build stages via an -# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). +# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). We use +# the same technique for the directory /tmp/wheels. # COPY requirements.txt requirements.txt ARG PIP_CACHE_DIR=/tmp/pip-cache @@ -56,7 +60,7 @@ RUN apt-get update \ tini \ && rm -rf /var/lib/apt/lists/* -# install wheels built in the build-stage +# install wheels built in the build stage COPY requirements.txt /tmp/requirements.txt ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 420e4f9a22..58a1d273d5 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -7,12 +7,16 @@ # This stage is building Python wheels for use in later stages by using a base # image that has more pre-requisites to do so, such as a C++ compiler. # +# NOTE: If the image version is updated, also update it in ci/refreeze and +# hub's Dockerfile! +# FROM python:3.11-bullseye as build-stage # Build wheels # # We set pip's cache directory and expose it across build stages via an -# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). +# ephemeral docker cache (--mount=type=cache,target=${PIP_CACHE_DIR}). We use +# the same technique for the directory /tmp/wheels. # COPY requirements.txt requirements.txt ARG PIP_CACHE_DIR=/tmp/pip-cache From ba04ded37df4a3da5fbe325dc20bd1299c79bf2a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 28 Feb 2023 09:50:18 +0100 Subject: [PATCH 657/898] docs: update changelog to reflect k8s 1.22+ is required --- docs/source/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index c9103c6a5f..31c297c8ab 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,7 +12,7 @@ and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. -- K8s 1.21 is now required. +- K8s 1.22 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. ## 2.0 From 74b1e3d9784be196539f407e0634adbaae725fa1 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 3 Mar 2023 05:15:39 +0000 Subject: [PATCH 658/898] Update jupyterhub from 3.1.1 to 4.0.0b1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 22 +++++++--------- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 32 +++++++++++------------ jupyterhub/Chart.yaml | 2 +- 5 files changed, 28 insertions(+), 32 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index d5a788ccec..d8ba170b3d 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.1.1 +jupyterhub==4.0.0b1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 049c6b92a3..e635a4902f 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.9.3 +alembic==1.9.4 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -34,12 +34,10 @@ charset-normalizer==3.0.1 # via # aiohttp # requests -cryptography==39.0.1 +cryptography==39.0.2 # via pyopenssl escapism==1.0.1 - # via - # jupyterhub-kubespawner - # jupyterhub-ltiauthenticator + # via jupyterhub-kubespawner frozenlist==1.3.3 # via # aiohttp @@ -60,7 +58,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.1.1 +jupyterhub==4.0.0b1 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -80,7 +78,7 @@ jupyterhub-kubespawner==4.3.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.2.0 +jupyterhub-ltiauthenticator==1.4.0 # via -r requirements.in jupyterhub-nativeauthenticator==1.1.0 # via -r requirements.in @@ -143,9 +141,9 @@ python-dateutil==2.8.2 # jupyterhub # jupyterhub-idle-culler # kubernetes-asyncio -python-json-logger==2.0.5 +python-json-logger==2.0.7 # via jupyter-telemetry -python-slugify==8.0.0 +python-slugify==8.0.1 # via jupyterhub-kubespawner pyyaml==6.0 # via @@ -169,12 +167,12 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.3 +sqlalchemy==2.0.4 # via # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==1.4.4 +sqlalchemy-cockroachdb==2.0.0 # via -r requirements.in statsd==4.0.1 # via -r requirements.in @@ -190,7 +188,7 @@ traitlets==5.9.0 # jupyter-telemetry # jupyterhub # jupyterhub-ldapauthenticator -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via sqlalchemy urllib3==1.26.14 # via diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 785e00e368..c7bd879fee 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==3.1.1 +jupyterhub==4.0.0b1 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 97ae99a106..836b168171 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.9.3 +alembic==1.9.4 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -21,7 +21,7 @@ async-generator==1.10 # via jupyterhub attrs==22.2.0 # via jsonschema -babel==2.11.0 +babel==2.12.1 # via jupyterlab-server backcall==0.2.0 # via ipython @@ -41,7 +41,7 @@ charset-normalizer==3.0.1 # via requests comm==0.1.2 # via ipykernel -cryptography==39.0.1 +cryptography==39.0.2 # via pyopenssl debugpy==1.6.6 # via ipykernel @@ -51,7 +51,7 @@ defusedxml==0.7.1 # via nbconvert executing==1.2.0 # via stack-data -fastjsonschema==2.16.2 +fastjsonschema==2.16.3 # via nbformat greenlet==2.0.2 # via sqlalchemy @@ -59,11 +59,11 @@ idna==3.4 # via # anyio # requests -ipykernel==6.21.1 +ipykernel==6.21.2 # via # nbclassic # notebook -ipython==8.10.0 +ipython==8.11.0 # via # ipykernel # jupyterlab @@ -89,7 +89,7 @@ jsonschema==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==8.0.2 +jupyter-client==8.0.3 # via # ipykernel # jupyter-server @@ -107,7 +107,7 @@ jupyter-core==5.2.0 # nbconvert # nbformat # notebook -jupyter-server==1.23.5 +jupyter-server==1.23.6 # via # jupyterlab # jupyterlab-server @@ -117,7 +117,7 @@ jupyter-server==1.23.5 # retrolab jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==3.1.1 +jupyterhub==4.0.0b1 # via -r requirements.in jupyterlab==3.4.8 # via @@ -142,7 +142,7 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.1 +nbclassic==0.5.2 # via # -r requirements.in # jupyterlab @@ -203,7 +203,7 @@ prometheus-client==0.16.0 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.36 +prompt-toolkit==3.0.38 # via ipython psutil==5.9.4 # via ipykernel @@ -227,10 +227,8 @@ python-dateutil==2.8.2 # via # jupyter-client # jupyterhub -python-json-logger==2.0.5 +python-json-logger==2.0.7 # via jupyter-telemetry -pytz==2022.7.1 - # via babel pyzmq==25.0.0 # via # ipykernel @@ -258,9 +256,9 @@ six==1.16.0 # python-dateutil sniffio==1.3.0 # via anyio -soupsieve==2.3.2.post1 +soupsieve==2.4 # via beautifulsoup4 -sqlalchemy==2.0.3 +sqlalchemy==2.0.4 # via # alembic # jupyterhub @@ -303,7 +301,7 @@ traitlets==5.9.0 # nbconvert # nbformat # notebook -typing-extensions==4.4.0 +typing-extensions==4.5.0 # via sqlalchemy urllib3==1.26.14 # via requests diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index eed73756b5..bc8d2fd254 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "3.1.1" +appVersion: "4.0.0b1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 9bc342d29ace0c4a2e1dde69cbfabad057fb7c46 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 2 Mar 2023 14:21:23 +0100 Subject: [PATCH 659/898] ci: update ci/refreeze script with new recommended behavior --- ci/refreeze | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/refreeze b/ci/refreeze index 13b46df7d9..97abf6a1e1 100755 --- a/ci/refreeze +++ b/ci/refreeze @@ -14,6 +14,6 @@ for img in ${IMAGES}; do --workdir=/io \ --user=root \ python:3.11-bullseye \ - sh -c 'pip install pip-tools==6.* && pip-compile --upgrade' + sh -c 'pip install pip-tools==6.* && pip-compile --resolver=backtracking --upgrade' popd done From 4fa8b47a3ea30ba940edfc7d596c576ad5fe7995 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 2 Mar 2023 14:22:08 +0100 Subject: [PATCH 660/898] image, singleuser-sample: refreeze --- images/singleuser-sample/requirements.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 836b168171..8dd9c75a75 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -58,6 +58,7 @@ greenlet==2.0.2 idna==3.4 # via # anyio + # jsonschema # requests ipykernel==6.21.2 # via @@ -109,6 +110,7 @@ jupyter-core==5.2.0 # notebook jupyter-server==1.23.6 # via + # jupyter-server-fileid # jupyterlab # jupyterlab-server # nbclassic @@ -225,10 +227,13 @@ pyrsistent==0.19.3 # via jsonschema python-dateutil==2.8.2 # via + # arrow # jupyter-client # jupyterhub python-json-logger==2.0.7 - # via jupyter-telemetry + # via + # jupyter-events + # jupyter-telemetry pyzmq==25.0.0 # via # ipykernel @@ -254,6 +259,7 @@ six==1.16.0 # asttokens # bleach # python-dateutil + # rfc3339-validator sniffio==1.3.0 # via anyio soupsieve==2.4 @@ -292,6 +298,7 @@ traitlets==5.9.0 # ipython # jupyter-client # jupyter-core + # jupyter-events # jupyter-server # jupyter-telemetry # jupyterhub From d57cd55f0254d8c0ca12c4d799e50cd30df3092e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 2 Mar 2023 14:36:18 +0100 Subject: [PATCH 661/898] image, singleuser-sample: remove retrolab and refreeze --- images/singleuser-sample/requirements.in | 1 - images/singleuser-sample/requirements.txt | 69 +++++++++++++++++------ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index c7bd879fee..8b0b548f5a 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -12,7 +12,6 @@ jupyterhub==4.0.0b1 # UI jupyterlab nbclassic -retrolab # plugins nbgitpuller diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 8dd9c75a75..30997eac99 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,6 +4,10 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # +aiofiles==22.1.0 + # via ypy-websocket +aiosqlite==0.18.0 + # via ypy-websocket alembic==1.9.4 # via jupyterhub anyio==3.6.2 @@ -15,6 +19,8 @@ argon2-cffi==21.3.0 # notebook argon2-cffi-bindings==21.2.0 # via argon2-cffi +arrow==1.2.3 + # via isoduration asttokens==2.2.1 # via stack-data async-generator==1.10 @@ -53,6 +59,8 @@ executing==1.2.0 # via stack-data fastjsonschema==2.16.3 # via nbformat +fqdn==1.5.1 + # via jsonschema greenlet==2.0.2 # via sqlalchemy idna==3.4 @@ -72,6 +80,8 @@ ipython-genutils==0.2.0 # via # nbclassic # notebook +isoduration==20.11.0 + # via jsonschema jedi==0.18.2 # via ipython jinja2==3.1.2 @@ -85,8 +95,11 @@ jinja2==3.1.2 # notebook json5==0.9.11 # via jupyterlab-server -jsonschema==4.17.3 +jsonpointer==2.3 + # via jsonschema +jsonschema[format-nongpl]==4.17.3 # via + # jupyter-events # jupyter-telemetry # jupyterlab-server # nbformat @@ -108,7 +121,11 @@ jupyter-core==5.2.0 # nbconvert # nbformat # notebook -jupyter-server==1.23.6 +jupyter-events==0.6.3 + # via + # jupyter-server + # jupyter-server-fileid +jupyter-server==2.3.0 # via # jupyter-server-fileid # jupyterlab @@ -116,21 +133,26 @@ jupyter-server==1.23.6 # nbclassic # nbgitpuller # notebook-shim - # retrolab +jupyter-server-fileid==0.8.0 + # via jupyter-server-ydoc +jupyter-server-terminals==0.4.4 + # via jupyter-server +jupyter-server-ydoc==0.6.1 + # via jupyterlab jupyter-telemetry==0.1.0 # via jupyterhub +jupyter-ydoc==0.2.2 + # via + # jupyter-server-ydoc + # jupyterlab jupyterhub==4.0.0b1 # via -r requirements.in -jupyterlab==3.4.8 - # via - # -r requirements.in - # retrolab +jupyterlab==3.6.1 + # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert jupyterlab-server==2.19.0 - # via - # jupyterlab - # retrolab + # via jupyterlab mako==1.2.4 # via alembic markupsafe==2.1.2 @@ -149,7 +171,6 @@ nbclassic==0.5.2 # -r requirements.in # jupyterlab # notebook - # retrolab nbclient==0.7.2 # via nbconvert nbconvert==7.2.9 @@ -234,6 +255,8 @@ python-json-logger==2.0.7 # via # jupyter-events # jupyter-telemetry +pyyaml==6.0 + # via jupyter-events pyzmq==25.0.0 # via # ipykernel @@ -245,8 +268,14 @@ requests==2.28.2 # via # jupyterhub # jupyterlab-server -retrolab==0.3.21 - # via -r requirements.in +rfc3339-validator==0.1.4 + # via + # jsonschema + # jupyter-events +rfc3986-validator==0.1.1 + # via + # jsonschema + # jupyter-events ruamel-yaml==0.17.21 # via jupyter-telemetry send2trash==1.8.0 @@ -273,12 +302,11 @@ stack-data==0.6.2 terminado==0.17.1 # via # jupyter-server + # jupyter-server-terminals # nbclassic # notebook tinycss2==1.2.1 # via nbconvert -tomli==2.0.1 - # via jupyterlab tornado==6.2 # via # ipykernel @@ -289,7 +317,6 @@ tornado==6.2 # nbclassic # nbgitpuller # notebook - # retrolab # terminado traitlets==5.9.0 # via @@ -310,13 +337,23 @@ traitlets==5.9.0 # notebook typing-extensions==4.5.0 # via sqlalchemy +uri-template==1.2.0 + # via jsonschema urllib3==1.26.14 # via requests wcwidth==0.2.6 # via prompt-toolkit +webcolors==1.12 + # via jsonschema webencodings==0.5.1 # via # bleach # tinycss2 websocket-client==1.5.1 # via jupyter-server +y-py==0.5.9 + # via + # jupyter-ydoc + # ypy-websocket +ypy-websocket==0.8.2 + # via jupyter-server-ydoc From e86a0f845cd0e887d3773bfe8fc446e6489f5325 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 3 Mar 2023 12:58:24 +0100 Subject: [PATCH 662/898] docs: add note about the breaking change in the changelog --- docs/source/changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 31c297c8ab..21c221a4ee 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,6 +14,10 @@ this list should be updated. - K8s 1.22 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. +- JupyterHub 3.0.0 is upgraded to 4.0.0b1. + - The upgrade does not require restarts of single-user servers', and they can + have either jupyterhub 3 or 4 installed both before and after the upgrade. + - Changelog available at https://jupyterhub.readthedocs.io/en/latest/changelog.html ## 2.0 From 84f967ef99dac17b99c33568fe34d5ff06276c2e Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 3 Mar 2023 13:59:58 +0000 Subject: [PATCH 663/898] docs: user-env default image is not base-image --- docs/source/jupyterhub/customizing/user-environment.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-environment.md b/docs/source/jupyterhub/customizing/user-environment.md index 8f7a5f09cb..8c9b2c4017 100644 --- a/docs/source/jupyterhub/customizing/user-environment.md +++ b/docs/source/jupyterhub/customizing/user-environment.md @@ -23,14 +23,15 @@ To get started customizing the user environment, see the topics below. ## Choose and use an existing Docker image +This chart uses a minimal default singleuser image intended for quick tests. +You will need to choose a different image or build your own for real use. + Project Jupyter maintains the [jupyter/docker-stacks repository](https://github.com/jupyter/docker-stacks/), which contains ready to use Docker images. Each image includes a set of commonly used science and data science libraries and tools. They also provide excellent documentation on [how to choose a suitable image](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html). -If you wish to use another image from jupyter/docker-stacks than the -[base-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-base-notebook) -used by default, such as the [datascience-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook) +For example, to use the [datascience-notebook](https://jupyter-docker-stacks.readthedocs.io/en/latest/using/selecting.html#jupyter-datascience-notebook) image containing useful tools and libraries for data science, complete these steps: 1. Modify your `config.yaml` file to specify the image. For example: From 74497f569d8d5f37a807db2e288c4ccd03c9bfb7 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 3 Mar 2023 17:01:32 +0000 Subject: [PATCH 664/898] docs: Replace most permanent-redirects from linkcheck Replaces most `redirect ... - permanently to` reports from linkcheck with the destination, except for those where the redirect appears to be context dependent (e.g. country specific), and for anything in the changelog --- docs/source/administrator/authentication.md | 4 ++-- docs/source/administrator/upgrading/index.md | 4 ++-- .../jupyterhub/customizing/user-resources.md | 2 +- .../kubernetes/amazon/step-zero-aws-eks.md | 2 +- docs/source/kubernetes/google/step-zero-gcp.md | 4 ++-- .../kubernetes/microsoft/step-zero-azure.md | 16 ++++++++-------- docs/source/resources/glossary.md | 2 +- docs/source/resources/reference-docs.md | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index a5e7305b62..67c2fafe9a 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -198,7 +198,7 @@ The narrower scope `read:user` is sufficient for a configuration of `allowed_org The broader scope `read:org` doesn't have the limitations of `read:user`, but will require a one-off approval by the admins of the GitHub organizations' listed in `allowed_organizations`. This kind of approval can be requested by organization users [as documented on GitHub](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-personal-account-on-github/managing-your-membership-in-organizations/requesting-organization-approval-for-oauth-apps). -For details about GitHub scopes, see [GitHub's documentation](https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps). +For details about GitHub scopes, see [GitHub's documentation](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps). ``` #### Google @@ -300,7 +300,7 @@ hub: #### Azure Active Directory -[Azure Active Directory](https://docs.microsoft.com/en-us/azure/active-directory/) +[Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/) is an identity provider from Microsoft Azure. Apart from needing a OAuth2 _client id_ and _client secret_, you will also need a _tenant id_. diff --git a/docs/source/administrator/upgrading/index.md b/docs/source/administrator/upgrading/index.md index d0c4905f0a..54eddb72d5 100644 --- a/docs/source/administrator/upgrading/index.md +++ b/docs/source/administrator/upgrading/index.md @@ -11,7 +11,7 @@ then follow [](helm-upgrade-command). Major releases may contain breaking changes, and will often require changes to your configuration. They have dedicated instructions for upgrading your deployment in addition to the general instructions on this page. -For additional help, feel free to reach out to us on [gitter](https://gitter.im/jupyterhub/jupyterhub) +For additional help, feel free to reach out to us on [gitter](https://app.gitter.im/#/room/#jupyterhub_jupyterhub:gitter.im) or the [Discourse forum](https://discourse.jupyter.org/). (upgrading-major-upgrades)= @@ -113,7 +113,7 @@ Update the configuration to use this new image, which is typically done via ## JupyterHub versions installed in each Helm Chart Each Helm Chart is packaged with a specific version of JupyterHub (and -other software as well). See the [Helm Chart repository](https://jupyterhub.github.io/helm-chart/) for +other software as well). See the [Helm Chart repository](https://hub.jupyter.org/helm-chart/) for information about the versions of relevant software packages. ## Troubleshooting diff --git a/docs/source/jupyterhub/customizing/user-resources.md b/docs/source/jupyterhub/customizing/user-resources.md index 595950fd58..9d05b785d3 100644 --- a/docs/source/jupyterhub/customizing/user-resources.md +++ b/docs/source/jupyterhub/customizing/user-resources.md @@ -95,7 +95,7 @@ provider. Here are a few links to help you get started: - [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine/docs/how-to/gpus) - [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/blogs/compute/running-gpu-accelerated-kubernetes-workloads-on-p3-and-p2-ec2-instances-with-amazon-eks/) -- [Azure Kubernetes Service (AKS)](https://docs.microsoft.com/en-us/azure/aks/gpu-cluster) +- [Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/gpu-cluster) You will also need to deploy the k8s-device-plugin following the instructions [here](https://github.com/NVIDIA/k8s-device-plugin#quick-start). diff --git a/docs/source/kubernetes/amazon/step-zero-aws-eks.md b/docs/source/kubernetes/amazon/step-zero-aws-eks.md index de2ef74257..91ed1dacb5 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws-eks.md +++ b/docs/source/kubernetes/amazon/step-zero-aws-eks.md @@ -89,4 +89,4 @@ This guide uses AWS to set up a cluster. This mirrors the steps found at [Gettin If you'd like to do some {ref}`optimizations `, you need to deploy Cluster Autoscaler (CA) first. -See +See diff --git a/docs/source/kubernetes/google/step-zero-gcp.md b/docs/source/kubernetes/google/step-zero-gcp.md index 683bdc3e7d..ead03588e0 100644 --- a/docs/source/kubernetes/google/step-zero-gcp.md +++ b/docs/source/kubernetes/google/step-zero-gcp.md @@ -5,7 +5,7 @@ [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/) (GKE) is the simplest and most common way of setting up a Kubernetes Cluster. You may be able to receive [free credits](https://cloud.google.com/free/) for trying it out (though note that a -free account [comes with limitations](https://cloud.google.com/free/docs/gcp-free-tier#free-tier-usage-limits)). +free account [comes with limitations](https://cloud.google.com/free/docs/free-cloud-features#free-tier-usage-limits)). Either way, you will need to connect your credit card or other payment method to your google cloud account. @@ -68,7 +68,7 @@ your google cloud account. - Replace `` with a name that can be used to refer to this cluster in the future. - `--machine-type` specifies the amount of CPU and RAM in each node within - this default node pool. There is a [variety of types](https://cloud.google.com/compute/docs/machine-types) to choose from. + this default node pool. There is a [variety of types](https://cloud.google.com/compute/docs/machine-resource) to choose from. - `--num-nodes` specifies how many nodes to spin up. You can change this later through the cloud console or using the `gcloud` command line tool. - `--zone` specifies the data center zone where your cluster will be created. diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index f7aea7b001..c08ca72d50 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -2,10 +2,10 @@ # Kubernetes on Microsoft Azure Kubernetes Service (AKS) -You can create a Kubernetes cluster [either through the Azure portal website, or using the Azure command line tools](https://docs.microsoft.com/en-us/azure/aks/). +You can create a Kubernetes cluster [either through the Azure portal website, or using the Azure command line tools](https://learn.microsoft.com/en-us/azure/aks/). This page describes the commands required to setup a Kubernetes cluster using the command line. -If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://docs.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal). +If you prefer to use the Azure portal see the [Azure Kubernetes Service quickstart](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-portal). 1. Prepare your Azure shell environment. You have two options, one is to use the Azure interactive shell, the other is to install the Azure command-line @@ -77,7 +77,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta `` of `ucb_2018sp_data100_hub`. - `--location` specifies the location of the data center you want your resource to be in. In this case, we used the `centralus` location. For other options, see the - [Azure list of locations that support AKS](https://docs.microsoft.com/en-us/azure/aks/quotas-skus-regions#region-availability). + [Azure list of locations that support AKS](https://learn.microsoft.com/en-us/azure/aks/quotas-skus-regions#region-availability). Note that not all locations offer all VM sizes. To see a list of recommended locations, go to [Azure Portal > Virtual Machines](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Compute%2FVirtualMachines), click on "create.." and see the list of recommended locations in the drop down list for `Region`. @@ -123,9 +123,9 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta Kubernetes does not by default come with a controller that enforces `networkpolicy` resources. `networkpolicy` resources are important as they define how Kubernetes pods can securely communicate with one another and the outside sources, for example, the internet. - To enable this in Azure, we must first create a [Virtual Network](https://docs.microsoft.com/en-gb/azure/virtual-network/virtual-networks-overview) with Azure's own network policies enabled. + To enable this in Azure, we must first create a [Virtual Network](https://learn.microsoft.com/en-gb/azure/virtual-network/virtual-networks-overview) with Azure's own network policies enabled. - This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://docs.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://projectcalico.docs.tigera.io/) network policies. + This section of the documentation is following the Microsoft Azure tutorial on [creating an AKS cluster and enabling network policy](https://learn.microsoft.com/en-us/azure/aks/use-network-policies#create-an-aks-cluster-and-enable-network-policy), which includes information on using [Calico](https://docs.tigera.io/) network policies. ``` az network vnet create \ @@ -160,7 +160,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta --output tsv) ``` - We will create an Azure Active Directory (Azure AD) [service principal](https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals) for use with the cluster, and assign the [Contributor role](https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor) for use with the VNet. + We will create an Azure Active Directory (Azure AD) [service principal](https://learn.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals) for use with the cluster, and assign the [Contributor role](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#contributor) for use with the VNet. Make sure `SERVICE-PRINCIPAL-NAME` is something recognisable, for example, `binderhub-sp`. ``` @@ -215,7 +215,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta - `--node-count` is the number of nodes you want in your Kubernetes cluster - `--node-vm-size` is the size of the nodes you want to use, which varies based on what you are using your cluster for and how much RAM/CPU each of your users need. - There is a [list of all possible node sizes](https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-sizes-specs) + There is a [list of all possible node sizes](https://learn.microsoft.com/en-us/azure/cloud-services/cloud-services-sizes-specs) for you to choose from, but not all might be available in your location. If you get an error whilst creating the cluster you can try changing either the region or the node size. - `--service-principal` is the application ID of the service principal we created @@ -354,4 +354,4 @@ RBAC is enabled by default when using the command line tools. Congrats. Now that you have your Kubernetes cluster running, it's time to begin {ref}`setup-helm`. -[azure resource group]: https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview#resource-groups +[azure resource group]: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-overview#resource-groups diff --git a/docs/source/resources/glossary.md b/docs/source/resources/glossary.md index 37f7e5e846..d07df8f397 100644 --- a/docs/source/resources/glossary.md +++ b/docs/source/resources/glossary.md @@ -78,7 +78,7 @@ Kubernetes for that cloud. - [The Illustrated Children's Guide to Kubernetes](https://www.youtube.com/watch?v=4ht22ReBjno) - - [The official "What is Kubernetes?" text](https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/) + - [The official "What is Kubernetes?" text](https://kubernetes.io/docs/concepts/overview/) Kubernetes API server The [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/) server, diff --git a/docs/source/resources/reference-docs.md b/docs/source/resources/reference-docs.md index 9c3f91ceda..e2c99d3c0a 100644 --- a/docs/source/resources/reference-docs.md +++ b/docs/source/resources/reference-docs.md @@ -6,6 +6,6 @@ provides information about JupyterHub itself (not the Kubernetes deployment). - [Binder](https://mybinder.org) allows users to create sharable computational environments on-the-fly. It makes heavy use of JupyterHub. -- The [2016 JupyterHub Workshop](https://github.com/jupyter-resources/jupyterhub-2016-workshop) +- The [2016 JupyterHub Workshop](https://github.com/jupyter/jupyterhub-2016-workshop) was an informal gathering to share experience in deploying JupyterHub for various use-cases, including teaching and high-performance computing. From 7dcf47974ab6675decdc674d8507770bc300d644 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 5 Mar 2023 12:01:57 +0100 Subject: [PATCH 665/898] Apply suggestions from code review --- docs/source/changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 21c221a4ee..6e0ae4745b 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -15,8 +15,12 @@ this list should be updated. - K8s 1.22 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. - JupyterHub 3.0.0 is upgraded to 4.0.0b1. - - The upgrade does not require restarts of single-user servers', and they can - have either jupyterhub 3 or 4 installed both before and after the upgrade. + - Although it is not officially supported to run a JupyterHub server with a + major version different from the singleuser servers' `jupyterhub-singleuser` + version, it seems possible during this upgrade. We recommend your singleuser + images are upgraded as soon as practical, before or after the JupyterHub + chart is upgraded. We expect running user servers to be able to keep running + during the upgrade. - Changelog available at https://jupyterhub.readthedocs.io/en/latest/changelog.html ## 2.0 From 643c0f0c1a6e35ea5ef56e41fcd74ea4ea254f34 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 5 Mar 2023 12:07:10 +0100 Subject: [PATCH 666/898] image, singleuser-sample: refreeze a final time --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 30997eac99..d5cc33e1e9 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -218,7 +218,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.0.0 +platformdirs==3.1.0 # via jupyter-core prometheus-client==0.16.0 # via From 8de2d9895f2d4db2a461a328067cf2d8d1766b24 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 21:05:41 +0000 Subject: [PATCH 667/898] Use jupyterhub docs `stable` instead of `latest` https://jupyterhub.readthedocs.io/en/stable/ should be the most recent release, whereas latest will include unreleased changes on the main branch --- docs/source/administrator/authentication.md | 10 +++++----- docs/source/administrator/upgrading/upgrade-1-to-2.md | 2 +- docs/source/changelog.md | 2 +- docs/source/resources/glossary.md | 2 +- docs/source/resources/reference-docs.md | 2 +- jupyterhub/schema.yaml | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 67c2fafe9a..7640c2b6fd 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -39,10 +39,10 @@ authenticator class itself through this Helm chart's As all authenticator classes derive from the `Authenticator` base class, they share some configuration options. Below are some common configuration options, but please refer to the official [configuration -reference](https://jupyterhub.readthedocs.io/en/latest/api/auth.html) for more +reference](https://jupyterhub.readthedocs.io/en/stable/api/auth.html) for more details. -### [allowed_users](https://jupyterhub.readthedocs.io/en/latest/api/auth.html#jupyterhub.auth.Authenticator.allowed_users) / [admin_users](https://jupyterhub.readthedocs.io/en/latest/api/auth.html#jupyterhub.auth.LocalAuthenticator.admin_users) +### [allowed_users](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.allowed_users) / [admin_users](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.LocalAuthenticator.admin_users) Some authenticator classes may have dedicated logic in addition this this to authorize users. @@ -70,7 +70,7 @@ In the above configuration, we have configured three things: 2. anyone will be able to login with username `user1-4` and the password `a-shared-secret-password` 3. `user1` and `user2` will have admin permissions, while `user3` and `user4` will be regular users. -### [auto_login](https://jupyterhub.readthedocs.io/en/latest/api/auth.html#jupyterhub.auth.Authenticator.auto_login) +### [auto_login](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.auto_login) If you have configured authentication with GitHub for example, the page `/hub/login` will feature a single orange button that users are to press to @@ -84,7 +84,7 @@ hub: auto_login: true ``` -### [enable_auth_state](https://jupyterhub.readthedocs.io/en/latest/api/auth.html#jupyterhub.auth.Authenticator.enable_auth_state) +### [enable_auth_state](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.enable_auth_state) If you want JupyterHub to persist often sensitive information received as part of logging in, you need to enable it. @@ -97,7 +97,7 @@ hub: ``` For more information about authentication state, see [JupyterHub's own -documentation](https://jupyterhub.readthedocs.io/en/latest/reference/authenticators.html#authentication-state) +documentation](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html#authentication-state) about authentication state. ````{note} diff --git a/docs/source/administrator/upgrading/upgrade-1-to-2.md b/docs/source/administrator/upgrading/upgrade-1-to-2.md index e41004d025..edbec4b8f7 100644 --- a/docs/source/administrator/upgrading/upgrade-1-to-2.md +++ b/docs/source/administrator/upgrading/upgrade-1-to-2.md @@ -51,7 +51,7 @@ and the configuration reference entries under Z2JH 2.0.0 upgrades from JupyterHub 1 directly to JupyterHub 3, and also upgrades all hub components. If you are using any custom JupyterHub services, addons, API integrations, or extra configuration, you should review the breaking changes in the -major releases of JupyterHub 2 and 3 in the [JupyterHub changelog](https://jupyterhub.readthedocs.io/en/latest/changelog.html). +major releases of JupyterHub 2 and 3 in the [JupyterHub changelog](https://jupyterhub.readthedocs.io/en/stable/changelog.html). JupyterHub 2 and 3 updates the database schema, which means a migration takes place when you upgrade JupyterHub. Z2JH automatically handles the upgrade if you are using sqlite (`hub.db.type = 'sqlite-pvc'`, the default), but it may not be possible to downgrade to older releases after this. diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6e0ae4745b..c91bf80db3 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -21,7 +21,7 @@ this list should be updated. images are upgraded as soon as practical, before or after the JupyterHub chart is upgraded. We expect running user servers to be able to keep running during the upgrade. - - Changelog available at https://jupyterhub.readthedocs.io/en/latest/changelog.html + - Changelog available at https://jupyterhub.readthedocs.io/en/stable/changelog.html ## 2.0 diff --git a/docs/source/resources/glossary.md b/docs/source/resources/glossary.md index d07df8f397..bdd833e07c 100644 --- a/docs/source/resources/glossary.md +++ b/docs/source/resources/glossary.md @@ -16,7 +16,7 @@ details. A user who can access the JupyterHub admin panel. They can start/stop user pods, and potentially access their notebooks. -[authenticator](https://jupyterhub.readthedocs.io/en/latest/reference/authenticators.html) +[authenticator](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html) The way in which users are authenticated to log into JupyterHub. There are many authenticators available, like GitHub, Google, MediaWiki, Dummy (anyone can log in), etc. diff --git a/docs/source/resources/reference-docs.md b/docs/source/resources/reference-docs.md index e2c99d3c0a..116c2f20b7 100644 --- a/docs/source/resources/reference-docs.md +++ b/docs/source/resources/reference-docs.md @@ -2,7 +2,7 @@ # Related Projects -- The [JupyterHub Documentation](https://jupyterhub.readthedocs.io/en/latest/) +- The [JupyterHub Documentation](https://jupyterhub.readthedocs.io/en/stable/) provides information about JupyterHub itself (not the Kubernetes deployment). - [Binder](https://mybinder.org) allows users to create sharable computational environments on-the-fly. It makes heavy use of JupyterHub. diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 01f00ee26a..b224ccdf44 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1329,7 +1329,7 @@ properties: permissions as defined in JupyterHub's RBAC system. Complement this documentation with [JupyterHub's - documentation](https://jupyterhub.readthedocs.io/en/latest/rbac/roles.html#defining-roles) + documentation](https://jupyterhub.readthedocs.io/en/stable/rbac/roles.html#defining-roles) about `load_roles`. Note that while JupyterHub's native configuration `load_roles` accepts From 56e94bd729527d4a875a5176596708d5a3ff4117 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 21:12:37 +0000 Subject: [PATCH 668/898] Revert https://app.gitter.im/#/room/#jupyterhub_jupyterhub:gitter.im redirect https://gitter.im/jupyterhub/jupyterhub is a 301 permanent redirect but the linkchecker can't handle the destination since it's client-side dynamic content: `(administrator/upgrading/index: line 14) broken https://app.gitter.im/#/room/#jupyterhub_jupyterhub:gitter.im - Anchor '/room/#jupyterhub_jupyterhub:gitter.im' not found` --- docs/source/administrator/upgrading/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/upgrading/index.md b/docs/source/administrator/upgrading/index.md index 54eddb72d5..f9c6564252 100644 --- a/docs/source/administrator/upgrading/index.md +++ b/docs/source/administrator/upgrading/index.md @@ -11,7 +11,7 @@ then follow [](helm-upgrade-command). Major releases may contain breaking changes, and will often require changes to your configuration. They have dedicated instructions for upgrading your deployment in addition to the general instructions on this page. -For additional help, feel free to reach out to us on [gitter](https://app.gitter.im/#/room/#jupyterhub_jupyterhub:gitter.im) +For additional help, feel free to reach out to us on [gitter](https://gitter.im/jupyterhub/jupyterhub) or the [Discourse forum](https://discourse.jupyter.org/). (upgrading-major-upgrades)= From f5b1ec01b95efaa5240d9571df5e2a2545ea735f Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 21:58:32 +0000 Subject: [PATCH 669/898] Summarise linkcheck CI output It takes too long to read through the entire output to find out what needs fixing --- .github/workflows/test-docs.yaml | 5 +++++ ci/summarise-linkcheck-output | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100755 ci/summarise-linkcheck-output diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index a0a51a9184..fd7f321e31 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -43,3 +43,8 @@ jobs: run: | cd docs make linkcheck SPHINXOPTS='--color -W --keep-going' + + - name: summarise linkcheck issues + if: always() + run: | + ./ci/summarise-linkcheck-output ./docs/_build/linkcheck/output.json diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output new file mode 100755 index 0000000000..c4ed7c5d57 --- /dev/null +++ b/ci/summarise-linkcheck-output @@ -0,0 +1,19 @@ +#!/bin/bash +# Parse the output.json created by the Sphinx linkchecker +# and summarise broken and redirected links + +set -eu +LINKCHECK="$1" + +N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") + +if [ "$N_BROKEN" -gt 0 ]; then + printf "\n\033[31;1m%s\033[0m\n" "Broken links" + jq -r 'select(.status=="broken") | "\(.filename):\(.lineno) \(.uri)\n \(.info)"' "$LINKCHECK" +fi + +if [ "$N_REDIRECTED" -gt 0 ]; then + printf "\n\033[35;1m%s\033[0m\n" "Redirected links" + jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" +fi From 8575c3bd99439ad3f226250e2811d5f107b7e643 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:02:24 +0000 Subject: [PATCH 670/898] fix shell syntax --- ci/summarise-linkcheck-output | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index c4ed7c5d57..74f7a8f7c8 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -8,12 +8,12 @@ LINKCHECK="$1" N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") -if [ "$N_BROKEN" -gt 0 ]; then +if [ $N_BROKEN -gt 0 ]; then printf "\n\033[31;1m%s\033[0m\n" "Broken links" jq -r 'select(.status=="broken") | "\(.filename):\(.lineno) \(.uri)\n \(.info)"' "$LINKCHECK" fi -if [ "$N_REDIRECTED" -gt 0 ]; then +if [ $N_REDIRECTED -gt 0 ]; then printf "\n\033[35;1m%s\033[0m\n" "Redirected links" jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" fi From 97cbf41a983113327f9de8e63c3ca6a05017f2ce Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:05:46 +0000 Subject: [PATCH 671/898] Try using `let` to quieten shellcheck --- ci/summarise-linkcheck-output | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index 74f7a8f7c8..21ae888576 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -5,8 +5,8 @@ set -eu LINKCHECK="$1" -N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") -N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +let N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +let N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") if [ $N_BROKEN -gt 0 ]; then printf "\n\033[31;1m%s\033[0m\n" "Broken links" From 4b5b6aed0ed7397057ba71f7656297039f6d554c Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:08:12 +0000 Subject: [PATCH 672/898] Try using `[[ . ]]` to quieten shellcheck --- ci/summarise-linkcheck-output | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index 21ae888576..1766c8eb25 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -8,12 +8,12 @@ LINKCHECK="$1" let N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") let N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") -if [ $N_BROKEN -gt 0 ]; then +if [[ $N_BROKEN -gt 0 ]]; then printf "\n\033[31;1m%s\033[0m\n" "Broken links" jq -r 'select(.status=="broken") | "\(.filename):\(.lineno) \(.uri)\n \(.info)"' "$LINKCHECK" fi -if [ $N_REDIRECTED -gt 0 ]; then +if [[ $N_REDIRECTED -gt 0 ]]; then printf "\n\033[35;1m%s\033[0m\n" "Redirected links" jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" fi From 5b2ae982e6fd20a71d498a5a4b8572bc6df668f9 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:14:39 +0000 Subject: [PATCH 673/898] Give up, use `shellcheck disable=SC2086` --- ci/summarise-linkcheck-output | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index 1766c8eb25..e8551701fe 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -5,14 +5,16 @@ set -eu LINKCHECK="$1" -let N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") -let N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +# shellcheck disable=SC2086 if [[ $N_BROKEN -gt 0 ]]; then printf "\n\033[31;1m%s\033[0m\n" "Broken links" jq -r 'select(.status=="broken") | "\(.filename):\(.lineno) \(.uri)\n \(.info)"' "$LINKCHECK" fi +# shellcheck disable=SC2086 if [[ $N_REDIRECTED -gt 0 ]]; then printf "\n\033[35;1m%s\033[0m\n" "Redirected links" jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" From 972b5b5b85362c2569b4b03b63821e10cb9cdaaf Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:17:20 +0000 Subject: [PATCH 674/898] =?UTF-8?q?linkcheck-output:=20exit=20non-zero=20s?= =?UTF-8?q?o=20GH=20hopefully=20shows=20=E2=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ci/summarise-linkcheck-output | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index e8551701fe..07b5f930fa 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -19,3 +19,5 @@ if [[ $N_REDIRECTED -gt 0 ]]; then printf "\n\033[35;1m%s\033[0m\n" "Redirected links" jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" fi + +exit "$N_BROKEN" From a9c09d2cfdcce8c25d7c0574c2e1a26ac108f9d1 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 22:29:56 +0000 Subject: [PATCH 675/898] Fix counting --- ci/summarise-linkcheck-output | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index 07b5f930fa..4d3a994721 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -5,8 +5,8 @@ set -eu LINKCHECK="$1" -N_BROKEN=$(jq -r 'select(.status=="broken") | reduce inputs as $s(0;.+1)' "$LINKCHECK") -N_REDIRECTED=$(jq -r 'select(.status=="redirected") | reduce inputs as $s(0;.+1)' "$LINKCHECK") +N_BROKEN=$(jq -r 'select(.status=="broken")' "$LINKCHECK" | jq -s length) +N_REDIRECTED=$(jq -r 'select(.status=="redirected")' "$LINKCHECK" | jq -s length) # shellcheck disable=SC2086 if [[ $N_BROKEN -gt 0 ]]; then From 25f8d6b361670604756503455b4b3e5214c59055 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 5 Mar 2023 23:11:11 +0000 Subject: [PATCH 676/898] summarise-linkcheck-output: broken and perm-redirect only --- ci/summarise-linkcheck-output | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/summarise-linkcheck-output b/ci/summarise-linkcheck-output index 4d3a994721..864479337d 100755 --- a/ci/summarise-linkcheck-output +++ b/ci/summarise-linkcheck-output @@ -6,7 +6,7 @@ set -eu LINKCHECK="$1" N_BROKEN=$(jq -r 'select(.status=="broken")' "$LINKCHECK" | jq -s length) -N_REDIRECTED=$(jq -r 'select(.status=="redirected")' "$LINKCHECK" | jq -s length) +N_PERMANENT_REDIRECT=$(jq -r 'select(.status=="redirected")' "$LINKCHECK" | jq -s length) # shellcheck disable=SC2086 if [[ $N_BROKEN -gt 0 ]]; then @@ -15,9 +15,9 @@ if [[ $N_BROKEN -gt 0 ]]; then fi # shellcheck disable=SC2086 -if [[ $N_REDIRECTED -gt 0 ]]; then - printf "\n\033[35;1m%s\033[0m\n" "Redirected links" - jq -r 'select(.status=="redirected") | "\(.filename):\(.lineno) \(.uri)\n \(.code) \(.info)"' "$LINKCHECK" +if [[ $N_PERMANENT_REDIRECT -gt 0 ]]; then + printf "\n\033[35;1m%s\033[0m\n" "Permanently redirected links" + jq -r 'select(.status=="redirected" and .code==301) | "\(.filename):\(.lineno) \(.uri)\n \(.info)"' "$LINKCHECK" fi exit "$N_BROKEN" From 1ec9ded742106af70fd3cc1d3d467106e6d536f1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Mar 2023 03:46:12 +0000 Subject: [PATCH 677/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.4 → v3.0.0-alpha.6](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.4...v3.0.0-alpha.6) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 494ed95fce..a5ba361bc4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.4 + rev: v3.0.0-alpha.6 hooks: - id: prettier From eac19bbc9ebea304f40abcfcdfedc19813ecdec4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 12 Mar 2023 14:05:56 +0000 Subject: [PATCH 678/898] Replace microk8s with generic self-hosted doc --- docs/source/conf.py | 1 + .../kubernetes/other-infrastructure/.gitkeep | 0 .../step-zero-microk8s.md | 170 ------------------ .../other-infrastructure/step-zero-other.md | 23 +++ docs/source/kubernetes/setup-kubernetes.md | 2 +- 5 files changed, 25 insertions(+), 171 deletions(-) delete mode 100644 docs/source/kubernetes/other-infrastructure/.gitkeep delete mode 100644 docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md create mode 100644 docs/source/kubernetes/other-infrastructure/step-zero-other.md diff --git a/docs/source/conf.py b/docs/source/conf.py index 1e4caa690d..ebc54a2d96 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -215,6 +215,7 @@ def parse_schema(d, md=[], depth=0, pre=""): "microsoft/step-zero-azure-autoscale": "kubernetes/microsoft/step-zero-azure", "microsoft/step-zero-azure": "kubernetes/microsoft/step-zero-azure", "google/step-zero-gcp": "kubernetes/google/step-zero-gcp", + "kubernetes/other-infrastructure/step-zero-microk8s": "kubernetes/other-infrastructure/step-zero-other", "create-k8s-cluster": "kubernetes/setup-kubernetes", "turn-off": "jupyterhub/uninstall", "setup-jupyterhub": "jupyterhub/index", diff --git a/docs/source/kubernetes/other-infrastructure/.gitkeep b/docs/source/kubernetes/other-infrastructure/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md b/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md deleted file mode 100644 index 529032ff67..0000000000 --- a/docs/source/kubernetes/other-infrastructure/step-zero-microk8s.md +++ /dev/null @@ -1,170 +0,0 @@ -(baremetal-microk8s)= - -# Kubernetes on a Bare Metal Host with MicroK8s - -If you have server hardware available and a small enough user base it's possible to use [Canonical's MicroK8s](https://microk8s.io/) in place of a cloud vendor. - -```{warning} -With no ability to scale, users will not be able to access their notebooks when memory and CPU resources are exhausted. Read the section on resource planning and set resource limits accordingly. -``` - -This guide describes how to configure MicroK8s to work with Zero to JupyterHub for Kubernetes. - -## Procedure - -1. Install MicroK8s as described in the Getting Started section of the [MicroK8s documentation](https://microk8s.io/docs). - - ```{note} - For production use consider installing MicroK8s on Ubuntu. Other platforms use an Ubuntu VM which reduces the available resources and performance. - ``` - -1. Enable the necessary MicroK8s Add ons: - - - [dns](https://microk8s.io/docs/addon-dns) - - helm3 - - ``` - microk8s enable dns - microk8s enable helm3 - ``` - -1. Configure networking. - - The Zero to JupyterHub helm chart defaults to using the `LoadBalancer` service type. On cloud vendors this triggers the allocation of a load balancer and IP address. In order for a `LoadBalancer` resource to work in MicroK8s the [MetalLB add on](https://microk8s.io/docs/addon-metallb) has to be enabled. - - MetalLB has two modes: [Layer 2 Mode](https://metallb.universe.tf/concepts/layer2/), the default and recommended mode, and [BGP Mode](https://metallb.universe.tf/concepts/bgp/). In Layer 2 mode, MetalLB needs a range of IP addresses that are on the same network as the host running MicroK8s. If the host has multiple interfaces you can choose addresses from of _any_ of the interfaces. The range you give MetalLB can have as few as one IP address. When a `LoadBalancer` resource is requested MetalLB automatically adds one of its IP addresses to the interface of your host and passes the traffic into your Kubernetes system. This example shows how to enable a pool of addresses from `10.0.0.100` to `10.0.0.200`: - - ``` - microk8s enable metallb:10.0.0.100-10.0.0.200 - ``` - - If you give MetalLB a range of IP addresses you can choose one in your JupyterHub configuration. This is particularly important if you need TLS because you will have to setup a DNS entry for your server that has a fixed IP address. Here's an example proxy configuration with a fixed IP address request: - - ```yaml - ## Example config.yaml - proxy: - https: - enabled: true - hosts: - - jupyter.myschool.edu - letsencrypt: - contactEmail: me@myschool.edu - service: - loadBalancerIP: 10.0.0.150 - ``` - -1. Configure Storage. - - The JupyterHub chart uses persistent volume claims to allocate storage for notebooks and the hub database. Cloud vendors handle these claims automatically. On MicroK8s you have to enable the [OpenEBS Add-on](https://microk8s.io/docs/addon-openebs) so claims will be bound to storage. OpenEBS uses iSCSI for clustering which isn't necessary on a single host but the service must be enabled before you can enable OpenEBS: - - ``` - sudo systemctl enable iscsid.service - ``` - - Now you can enable OpenEBS: - - ``` - microk8s enable openebs - ``` - - OpenEBS installs a set of `StorageClass` resources but does not mark any of them default. Choose a directory on your host where you want to store data from your cluster. The path can be on the system disk or a separate disk. Create a YAML file called `local-storage-dir.yaml` with the following contents: - - ```yaml - ## local-storage-dir.yaml - apiVersion: storage.k8s.io/v1 - kind: StorageClass - metadata: - name: local-storage-dir - annotations: - storageclass.kubernetes.io/is-default-class: "true" - openebs.io/cas-type: local - cas.openebs.io/config: | - - name: StorageType - value: hostpath - - name: BasePath - value: /path/to/your/storage - provisioner: openebs.io/local - reclaimPolicy: Delete - volumeBindingMode: WaitForFirstConsumer - ``` - - ```{note} - Replace `/path/to/your/storage` with your desired path. - ``` - - Apply the customized `StorageClass` resource to your cluster: - - ``` - $ microk8s.kubectl apply -f local-storage-dir.yaml - ``` - - With a default storage class installed the Zero to JupyterHub chart doesn't require any customization. - -Now you're ready to install JupyterHub! - -## Resource Planning - -Hardware has to be sized for **peak load**. It's critical to plan for class size, especially if you intend to use JupyterHub in class with all attendees logged in simultaneously. Each running notebook server should be **guaranteed at least** 0.5 cores and 1G of RAM. Setting the limits lower will cause a lot of frustration. When a server exhausts its memory kernels die off and notebooks crash. The Kubernetes system and JupyterHub also consume resources that have to be accounted for. Assuming there are 2 cores and 2 GiB of RAM overhead this formula will tell you how to size a machine for a particular class size: - -```{math} -Cores = \left \lceil{2 + CPUGuarantee * ClassSize}\right \rceil - -RAM = 2 + RAMGuarantee * ClassSize -``` - -If you use the default limits in your configuration: - -```yaml -## Example config.yaml -singleuser: - memory: - guarantee: 1G - cpu: - guarantee: 0.5 -``` - -and you have a class of 35 students that means you should have a machine with at least: - -```{math} -Cores = \left \lceil{2 + 0.5 * 35}\right \rceil = 20 - -RAM = 2 + {1 * 35} = 37 -``` - -The default limits are fairly meager. If you intend to have your class doing real work or you expect that they will have many notebooks open simultaneously in JupyterLab you should plan for double the memory and CPU limits: - -```{math} -Cores = \left \lceil{2 + 1 * 35}\right \rceil = 37 - -RAM = 2 + {2 * 35} = 72 -``` - -These numbers are estimates. Disk usage and network bandwidth must also be considered. - -## Troubleshooting - -1. The JupyterHub `LoadBalancer` resource is stuck in the `Pending` state. - - Verify that you have MetalLB installed correctly and that you gave it IP addresses. This command should show you two running pods. Check the controller pod log for errors: - - ``` - microk8s.kubectl -n metallb-system get all - ``` - -1. The hub pod is stuck pending `Pending` state. - - This is probably because the volume claim is unbound. Use this command to check: - - ``` - microk8s.kubectl get pvc - ``` - - If the claim is unbound verify that the storage class you created is correct and set to the default. - - ``` - microk8s.kubectl get sc - ``` - -1. Not everyone in my class can start the notebook. - - Kubernetes manages resources carefully. Each notebook server is guaranteed CPU and memory resources and when there are no more to give new servers cannot be scheduled. Read the Resource Planning section and adjust your limits accordingly. diff --git a/docs/source/kubernetes/other-infrastructure/step-zero-other.md b/docs/source/kubernetes/other-infrastructure/step-zero-other.md new file mode 100644 index 0000000000..9a75cfb23c --- /dev/null +++ b/docs/source/kubernetes/other-infrastructure/step-zero-other.md @@ -0,0 +1,23 @@ +(self-hosted-k8s) + +# Self-hosted Kubernetes + +The Zero to JupyterHub guide assumes you're using a managed Kubernetes service with one of the main cloud platforms. + +JupyterHub can be deployed on a self-hosted Kubernetes cluster, but this is not officially supported. +However you may be able to get help, and find examples of other self-hosted deployments, on the [Jupyter community forum](https://discourse.jupyter.org/c/jupyterhub/10). + +## Kubernetes cluster requirements + +Z2JH assumes your Kubernetes cluster has the following features: + +- [Dynamic Volume Provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) for persistent storage +- [LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) or [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) for managing external access to JupyterHub + +Z2JH assumes you have full administrator access to the cluster. + +In many cases you need to consult your Kubernetes provider's documentation to find out how to enable these features. +Please test all these features with a simple deployment before proceeding with the Zero to JupyterHub guide. +Z2JH has several interacting components which makes it much more difficult to debug Kubernetes problems, so you will save a lot of time by verifying your cluster is working correctly first. + +It is possible to deploy Z2JH without some features, for example by [disabling persistent storage](schema_singleuser.storage.type) or using [NodePort](schema_proxy.service.type), but this is only suitable for testing. diff --git a/docs/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md index f6826174f3..c7e00d68d6 100644 --- a/docs/source/kubernetes/setup-kubernetes.md +++ b/docs/source/kubernetes/setup-kubernetes.md @@ -19,7 +19,7 @@ redhat/step-zero-openshift ibm/step-zero-ibm digital-ocean/step-zero-digital-ocean ovh/step-zero-ovh -other-infrastructure/step-zero-microk8s +other-infrastructure/step-zero-other ``` ```{note} From 048961f560cb3a217c22f87a1e704904f0da6a92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Mar 2023 05:56:42 +0000 Subject: [PATCH 679/898] build(deps): bump aquasecurity/trivy-action from 0.9.1 to 0.9.2 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.9.1 to 0.9.2. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/8bd2f9fbda2109502356ff8a6a89da55b1ead252...1f0aa582c8c8f5f7639610d6d38baddfea4fdcee) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0bcb4ae38d..40f88c2e3a 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 + uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 + uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@8bd2f9fbda2109502356ff8a6a89da55b1ead252 + uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee with: image-ref: rebuilt-image format: table From 4db21c0e9563176e0e0875db5c9030ba3d8fc06a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 15 Mar 2023 12:38:29 +0100 Subject: [PATCH 680/898] Reword comment on need to install pip packages as root --- docs/source/administrator/services.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index 1a4dd7553e..989328cffa 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -21,7 +21,8 @@ In the following snippet, I'm using a custom image that copies over the applicat # 2.0.0 is latest stable release at the time of this writing FROM jupyterhub/k8s-hub:2.0.0 -# The k8s-hub uses a multi-stage build to modify packages your build steps may need to run as root +# Depending on version, the k8s-hub image may have installed +# pip packages as root, forcing you to install as root as well USER root COPY ./service-fastapi /usr/src/fastapi RUN python3 -m pip install -r /usr/src/fastapi/requirements.txt From 150421671c6f4f8dc0e0f08805bcd9506a8fc3a5 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:41:57 +0000 Subject: [PATCH 681/898] Update jupyterhub from 4.0.0b1 to 4.0.0b2 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 14 +++++----- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 32 ++++++++++++----------- jupyterhub/Chart.yaml | 2 +- 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index d8ba170b3d..b23d973c22 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0b1 +jupyterhub==4.0.0b2 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e635a4902f..a7b9852ef3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.9.4 +alembic==1.10.2 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -30,7 +30,7 @@ certipy==0.1.3 # via jupyterhub cffi==1.15.1 # via cryptography -charset-normalizer==3.0.1 +charset-normalizer==3.1.0 # via # aiohttp # requests @@ -58,7 +58,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.0b1 +jupyterhub==4.0.0b2 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -167,7 +167,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.4 +sqlalchemy==2.0.6 # via # alembic # jupyterhub @@ -189,8 +189,10 @@ traitlets==5.9.0 # jupyterhub # jupyterhub-ldapauthenticator typing-extensions==4.5.0 - # via sqlalchemy -urllib3==1.26.14 + # via + # alembic + # sqlalchemy +urllib3==1.26.15 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 8b0b548f5a..13c07cc03f 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0b1 +jupyterhub==4.0.0b2 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index d5cc33e1e9..dfd6dabb88 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -8,7 +8,7 @@ aiofiles==22.1.0 # via ypy-websocket aiosqlite==0.18.0 # via ypy-websocket -alembic==1.9.4 +alembic==1.10.2 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -43,7 +43,7 @@ cffi==1.15.1 # via # argon2-cffi-bindings # cryptography -charset-normalizer==3.0.1 +charset-normalizer==3.1.0 # via requests comm==0.1.2 # via ipykernel @@ -68,7 +68,7 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.21.2 +ipykernel==6.21.3 # via # nbclassic # notebook @@ -125,7 +125,7 @@ jupyter-events==0.6.3 # via # jupyter-server # jupyter-server-fileid -jupyter-server==2.3.0 +jupyter-server==2.4.0 # via # jupyter-server-fileid # jupyterlab @@ -141,17 +141,17 @@ jupyter-server-ydoc==0.6.1 # via jupyterlab jupyter-telemetry==0.1.0 # via jupyterhub -jupyter-ydoc==0.2.2 +jupyter-ydoc==0.2.3 # via # jupyter-server-ydoc # jupyterlab -jupyterhub==4.0.0b1 +jupyterhub==4.0.0b2 # via -r requirements.in jupyterlab==3.6.1 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.19.0 +jupyterlab-server==2.20.0 # via jupyterlab mako==1.2.4 # via alembic @@ -166,14 +166,14 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.2 +nbclassic==0.5.3 # via # -r requirements.in # jupyterlab # notebook nbclient==0.7.2 # via nbconvert -nbconvert==7.2.9 +nbconvert==7.2.10 # via # jupyter-server # nbclassic @@ -192,7 +192,7 @@ nest-asyncio==1.5.6 # ipykernel # nbclassic # notebook -notebook==6.5.2 +notebook==6.5.3 # via # jupyterlab # nbgitpuller @@ -218,7 +218,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.1.0 +platformdirs==3.1.1 # via jupyter-core prometheus-client==0.16.0 # via @@ -257,7 +257,7 @@ python-json-logger==2.0.7 # jupyter-telemetry pyyaml==6.0 # via jupyter-events -pyzmq==25.0.0 +pyzmq==25.0.1 # via # ipykernel # jupyter-client @@ -293,7 +293,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4 # via beautifulsoup4 -sqlalchemy==2.0.4 +sqlalchemy==2.0.6 # via # alembic # jupyterhub @@ -336,10 +336,12 @@ traitlets==5.9.0 # nbformat # notebook typing-extensions==4.5.0 - # via sqlalchemy + # via + # alembic + # sqlalchemy uri-template==1.2.0 # via jsonschema -urllib3==1.26.14 +urllib3==1.26.15 # via requests wcwidth==0.2.6 # via prompt-toolkit diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index bc8d2fd254..20089f3941 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "4.0.0b1" +appVersion: "4.0.0b2" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From ba7af5fc71ddc74ee859eebc50f33e3874c540ab Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 16 Mar 2023 05:09:53 +0000 Subject: [PATCH 682/898] Update kube-scheduler version from v1.25.7 to v1.25.8 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 8acb9aa36a..ff9cb9073d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.25.7" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.8" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From b698c675e3c97db3ecfc49739c96eb812db4d82c Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 20 Mar 2023 05:14:26 +0000 Subject: [PATCH 683/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 15a414a2de..0214d36a20 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-02-13_05:13:57 +# VULN_SCAN_TIME=2023-03-20_05:14:23 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 5285ae3edc448c85f8c1c52635ab069f92fa858f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 05:56:43 +0000 Subject: [PATCH 684/898] build(deps): bump peter-evans/create-pull-request from 4.2.3 to 4.2.4 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.3 to 4.2.4. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/2b011faafdcbc9ceb11414d64d0573f37c774b04...38e0b6e68b4c852a5500a94740f0e535e0d7ba54) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 40f88c2e3a..13cb51e64e 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 + uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 108bb7e433..1fececb3c1 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 + uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -183,7 +183,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 + uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -216,7 +216,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 + uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 0690729cf7f1ce3b32613ac07a9b767d3a063226 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 20 Mar 2023 06:25:44 +0000 Subject: [PATCH 685/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index a7b9852ef3..30a947d0eb 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -167,7 +167,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.6 +sqlalchemy==2.0.7 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index dfd6dabb88..6915600a39 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -110,7 +110,7 @@ jupyter-client==8.0.3 # nbclassic # nbclient # notebook -jupyter-core==5.2.0 +jupyter-core==5.3.0 # via # ipykernel # jupyter-client @@ -125,7 +125,7 @@ jupyter-events==0.6.3 # via # jupyter-server # jupyter-server-fileid -jupyter-server==2.4.0 +jupyter-server==2.5.0 # via # jupyter-server-fileid # jupyterlab @@ -293,7 +293,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4 # via beautifulsoup4 -sqlalchemy==2.0.6 +sqlalchemy==2.0.7 # via # alembic # jupyterhub From c9c18124231162ff96c5777251d99bbf00d027df Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 22 Mar 2023 05:10:03 +0000 Subject: [PATCH 686/898] Update library/traefik version from v2.9.8 to v2.9.9 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index ff9cb9073d..5a3ccb86bf 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.8" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.9" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From af0a8db5f57390ec39eeaeb24a28ba36835a6ac5 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 22 Mar 2023 11:12:48 +0100 Subject: [PATCH 687/898] compile psycopg2 in hub image psycopg2-binary is not recommended for production --- images/hub/Dockerfile | 2 ++ images/hub/requirements.in | 2 +- images/hub/requirements.txt | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 61402f52bf..0ddc0c230e 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -55,6 +55,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ # requirement for pycurl libcurl4 \ + # requirement for using postgres database + libpq5 \ # requirement for using a local sqlite database sqlite3 \ tini \ diff --git a/images/hub/requirements.in b/images/hub/requirements.in index b23d973c22..a368bb2213 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -28,7 +28,7 @@ jupyterhub-kubespawner ## Other optional dependencies for additional features pymysql # mysql -psycopg2-binary # postgres +psycopg2 # postgres pycurl # internal http requests handle more load with pycurl sqlalchemy-cockroachdb # cocroachdb statsd # statsd metrics collection (TODO: remove soon, since folks use prometheus) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 30a947d0eb..593c7c3fe5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -118,7 +118,7 @@ pamela==1.0.0 # via jupyterhub prometheus-client==0.16.0 # via jupyterhub -psycopg2-binary==2.9.5 +psycopg2==2.9.5 # via -r requirements.in pyasn1==0.4.8 # via ldap3 From 0a84ecb0f974c229f1eacd0eab2314b2231bfe0f Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 22 Mar 2023 11:04:26 +0100 Subject: [PATCH 688/898] satisfy flake8 in jupyterhub_config.py there shouldn't be any lint config in pre-commit config, because then linters run via other means (e.g. editor config) will have different results --- .flake8 | 7 +++++++ .pre-commit-config.yaml | 8 -------- jupyterhub/files/hub/jupyterhub_config.py | 3 +++ 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..08e2c862d1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,7 @@ +[flake8] +# Ignore style and complexity +# E: style errors +# W: style warnings +# C: complexity +# D: docstring warnings (unused pydocstyle extension) +ignore = E, C, W, D diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a5ba361bc4..eae5aeae23 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -69,14 +69,6 @@ repos: rev: "6.0.0" hooks: - id: flake8 - # Ignore style and complexity - # E: style errors - # W: style warnings - # C: complexity - # - args: - - --ignore=E,C,W - - --builtins=c # pre-commit.ci config reference: https://pre-commit.ci/#configuration ci: diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 6fe2e90b94..e30be9aa34 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -1,3 +1,6 @@ +# load the config object (satisfies linters) +c = get_config() # noqa + import glob import os import re From fea45719b56c5935929ecfaf9dc8b23ec0dad085 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:59:18 +0000 Subject: [PATCH 689/898] hub image: refreeze requirements.txt --- images/singleuser-sample/requirements.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 6915600a39..8939ac8e6a 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -31,7 +31,7 @@ babel==2.12.1 # via jupyterlab-server backcall==0.2.0 # via ipython -beautifulsoup4==4.11.2 +beautifulsoup4==4.12.0 # via nbconvert bleach==6.0.0 # via nbconvert @@ -45,7 +45,7 @@ cffi==1.15.1 # cryptography charset-normalizer==3.1.0 # via requests -comm==0.1.2 +comm==0.1.3 # via ipykernel cryptography==39.0.2 # via pyopenssl @@ -68,7 +68,7 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.21.3 +ipykernel==6.22.0 # via # nbclassic # notebook @@ -103,7 +103,7 @@ jsonschema[format-nongpl]==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==8.0.3 +jupyter-client==8.1.0 # via # ipykernel # jupyter-server @@ -137,7 +137,7 @@ jupyter-server-fileid==0.8.0 # via jupyter-server-ydoc jupyter-server-terminals==0.4.4 # via jupyter-server -jupyter-server-ydoc==0.6.1 +jupyter-server-ydoc==0.8.0 # via jupyterlab jupyter-telemetry==0.1.0 # via jupyterhub @@ -147,7 +147,7 @@ jupyter-ydoc==0.2.3 # jupyterlab jupyterhub==4.0.0b2 # via -r requirements.in -jupyterlab==3.6.1 +jupyterlab==3.6.2 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert @@ -178,7 +178,7 @@ nbconvert==7.2.10 # jupyter-server # nbclassic # notebook -nbformat==5.7.3 +nbformat==5.8.0 # via # jupyter-server # nbclassic @@ -257,7 +257,7 @@ python-json-logger==2.0.7 # jupyter-telemetry pyyaml==6.0 # via jupyter-events -pyzmq==25.0.1 +pyzmq==25.0.2 # via # ipykernel # jupyter-client From 3ffe6d794585c44bfa4ede7b4e904b743c83b988 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 24 Mar 2023 11:11:07 +0100 Subject: [PATCH 690/898] ci: fix deprecation of set-output --- .github/workflows/publish.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9df4a9e4f8..00ccaead15 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -71,7 +71,8 @@ jobs: ): publishing = "true" print("Publishing chart") - print(f"::set-output name=publishing::{publishing}") + with open(os.environ["GITHUB_OUTPUT"], "a") as f: + f.write(f"publishing={publishing}\n") - name: Set up QEMU (for docker buildx) uses: docker/setup-qemu-action@v2 From 3b0e416f146e603984c6a2cf0f710ac40d7a17db Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 24 Mar 2023 11:12:34 +0100 Subject: [PATCH 691/898] ci: smaller opinionated refactoring --- .github/workflows/publish.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 00ccaead15..a204653c8f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -46,26 +46,20 @@ jobs: with: python-version: "3.8" - - name: store whether we are publishing the chart + - name: Decide to publish or not id: publishing shell: python - env: - REPO: ${{ github.repository }} - EVENT: ${{ github.event_name }} - REF: ${{ github.event.ref }} run: | import os - repo = os.environ["REPO"] - event = os.environ["EVENT"] - ref = os.environ["REF"] + repo = "${{ github.repository }}" + event = "${{ github.event_name }}" + ref = "${{ github.event.ref }}" publishing = "" if ( repo == "jupyterhub/zero-to-jupyterhub-k8s" and event == "push" and ( - # any tag ref.startswith("refs/tags/") - # or default branch or ref == "refs/heads/main" ) ): From 38133952eb81fb4c91e3afa64ace9594a4887492 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 24 Mar 2023 11:13:00 +0100 Subject: [PATCH 692/898] ci: systematic use of ubuntu 22.04 and python 3.11 --- .github/workflows/publish.yml | 4 ++-- .github/workflows/release-tag.yml | 2 +- .github/workflows/support-bot.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a204653c8f..665f3f2503 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,7 +34,7 @@ jobs: # ref: https://hub.docker.com/orgs/jupyterhub publish: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 with: @@ -44,7 +44,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.11" - name: Decide to publish or not id: publishing diff --git a/.github/workflows/release-tag.yml b/.github/workflows/release-tag.yml index 586b95a1f8..4327831409 100644 --- a/.github/workflows/release-tag.yml +++ b/.github/workflows/release-tag.yml @@ -9,7 +9,7 @@ on: jobs: create-release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: contents: write steps: diff --git a/.github/workflows/support-bot.yml b/.github/workflows/support-bot.yml index 571b829027..a65552ea36 100644 --- a/.github/workflows/support-bot.yml +++ b/.github/workflows/support-bot.yml @@ -10,7 +10,7 @@ permissions: jobs: action: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: dessant/support-requests@v3 with: diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index 592632915e..e91549c9b6 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -29,7 +29,7 @@ jobs: # - https://github.com/docker/build-push-action/blob/v2.3.0/docs/advanced/local-registry.md # - https://github.com/docker/build-push-action/blob/v2.3.0/docs/advanced/multi-platform.md build_images: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index fd7f321e31..8b8bddb44c 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -25,7 +25,7 @@ on: jobs: linkcheck: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 0bcb4ae38d..5dc26f2c21 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -32,7 +32,7 @@ on: jobs: trivy_image_scan: if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 environment: watch-dependencies strategy: diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 108bb7e433..2bf3719626 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -32,7 +32,7 @@ jobs: update-image-dependencies: # Don't run this job on forks if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 environment: watch-dependencies strategy: @@ -202,7 +202,7 @@ jobs: # these dependencies every day is too much noise. # if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s' && github.event_name != 'schedule' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 environment: watch-dependencies steps: From 81a70f8a27ad71b459f6593ca1c9ac497aa1c504 Mon Sep 17 00:00:00 2001 From: ChristofKaufmann Date: Sun, 26 Mar 2023 08:18:50 +0200 Subject: [PATCH 693/898] Fix invalid names in configuration examples --- docs/source/administrator/optimization.md | 2 +- jupyterhub/schema.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/optimization.md b/docs/source/administrator/optimization.md index a3eaaf55e7..d7857d8d05 100644 --- a/docs/source/administrator/optimization.md +++ b/docs/source/administrator/optimization.md @@ -164,7 +164,7 @@ singleuser: prePuller: extraImages: - myOtherImageIWantPulled: + my-other-image-i-want-pulled: name: jupyter/all-spark-notebook tag: 2343e33dec46 ``` diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index b224ccdf44..ac4cb8f7c8 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2776,7 +2776,7 @@ properties: ```yaml prePuller: extraImages: - myExtraImageIWantPulled: + my-extra-image-i-want-pulled: name: jupyter/all-spark-notebook tag: 2343e33dec46 ``` From 59225badd1e98bb8601178eb309f9502ca80d619 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Mar 2023 05:13:30 +0000 Subject: [PATCH 694/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 0214d36a20..5eaaef9e88 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-03-20_05:14:23 +# VULN_SCAN_TIME=2023-03-27_05:13:28 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From d346ea4069bcab61ebceec1e59a44623f7096e4e Mon Sep 17 00:00:00 2001 From: Simon Li Date: Thu, 30 Mar 2023 18:28:59 +0100 Subject: [PATCH 695/898] Replace IEC prefixes link The old URL intermittently fails the linkcheck --- docs/source/jupyterhub/customizing/user-storage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/jupyterhub/customizing/user-storage.md b/docs/source/jupyterhub/customizing/user-storage.md index 4c9d4d74ae..752a79d833 100644 --- a/docs/source/jupyterhub/customizing/user-storage.md +++ b/docs/source/jupyterhub/customizing/user-storage.md @@ -183,7 +183,7 @@ singleuser: This will request a `2Gi` volume per user. The default requests a `10Gi` volume per user. -We recommend you use the [IEC Prefixes](https://physics.nist.gov/cuu/Units/binary.html) +We recommend you use the [IEC Prefixes](https://en.wikipedia.org/wiki/Binary_prefix#Adoption_by_IEC,_NIST_and_ISO) (Ki, Mi, Gi, etc) for specifying how much storage you want. `2Gi` (IEC Prefix) is `(2 * 1024 * 1024 * 1024)` bytes, while `2G` (SI Prefix) is `(2 * 1000 * 1000 * 1000)` bytes. From 4043dc41495e199b1496f1417a44d4cae19d67f1 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:38:29 +0000 Subject: [PATCH 696/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 3be481e544..9239486b64 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2023-02-13_05:13:45 +# VULN_SCAN_TIME=2023-03-30_17:38:28 RUN apk add --no-cache iptables From 7859180b7f378562fe3bff90288a833806aef9ca Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:38:39 +0000 Subject: [PATCH 697/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- images/singleuser-sample/requirements.txt | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 593c7c3fe5..41303a6614 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -34,7 +34,7 @@ charset-normalizer==3.1.0 # via # aiohttp # requests -cryptography==39.0.2 +cryptography==40.0.1 # via pyopenssl escapism==1.0.1 # via jupyterhub-kubespawner @@ -130,9 +130,9 @@ pyjwt==2.6.0 # via # -r requirements.in # mwoauth -pymysql==1.0.2 +pymysql==1.0.3 # via -r requirements.in -pyopenssl==23.0.0 +pyopenssl==23.1.1 # via certipy pyrsistent==0.19.3 # via jsonschema diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 8939ac8e6a..dfdc3a5288 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -47,7 +47,7 @@ charset-normalizer==3.1.0 # via requests comm==0.1.3 # via ipykernel -cryptography==39.0.2 +cryptography==40.0.1 # via pyopenssl debugpy==1.6.6 # via ipykernel @@ -72,7 +72,7 @@ ipykernel==6.22.0 # via # nbclassic # notebook -ipython==8.11.0 +ipython==8.12.0 # via # ipykernel # jupyterlab @@ -151,7 +151,7 @@ jupyterlab==3.6.2 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.20.0 +jupyterlab-server==2.21.0 # via jupyterlab mako==1.2.4 # via alembic @@ -166,7 +166,7 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.3 +nbclassic==0.5.4 # via # -r requirements.in # jupyterlab @@ -218,7 +218,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.1.1 +platformdirs==3.2.0 # via jupyter-core prometheus-client==0.16.0 # via @@ -242,7 +242,7 @@ pygments==2.14.0 # via # ipython # nbconvert -pyopenssl==23.0.0 +pyopenssl==23.1.1 # via certipy pyrsistent==0.19.3 # via jsonschema @@ -345,7 +345,7 @@ urllib3==1.26.15 # via requests wcwidth==0.2.6 # via prompt-toolkit -webcolors==1.12 +webcolors==1.13 # via jsonschema webencodings==0.5.1 # via From af1a33a671eec11dcc116512eba6163194bdbb15 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:38:59 +0000 Subject: [PATCH 698/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 5eaaef9e88..6056cea343 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-03-27_05:13:28 +# VULN_SCAN_TIME=2023-03-30_17:38:57 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 00a12ab614574c95af381d9cbec384d5fc97eedd Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 31 Mar 2023 10:36:35 +0000 Subject: [PATCH 699/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 17 ++++++++++++----- images/singleuser-sample/requirements.txt | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 41303a6614..1e13576deb 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -35,9 +35,13 @@ charset-normalizer==3.1.0 # aiohttp # requests cryptography==40.0.1 - # via pyopenssl + # via + # pyjwt + # pyopenssl escapism==1.0.1 - # via jupyterhub-kubespawner + # via + # jupyterhub-kubespawner + # jupyterhub-ltiauthenticator frozenlist==1.3.3 # via # aiohttp @@ -78,7 +82,7 @@ jupyterhub-kubespawner==4.3.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.4.0 +jupyterhub-ltiauthenticator==1.5.0 # via -r requirements.in jupyterhub-nativeauthenticator==1.1.0 # via -r requirements.in @@ -103,7 +107,9 @@ mwoauth==0.3.8 nullauthenticator==1.0.0 # via -r requirements.in oauthenticator==15.1.0 - # via -r requirements.in + # via + # -r requirements.in + # jupyterhub-ltiauthenticator oauthlib==3.2.2 # via # jupyterhub @@ -126,9 +132,10 @@ pycparser==2.21 # via cffi pycurl==7.45.2 # via -r requirements.in -pyjwt==2.6.0 +pyjwt[crypto]==2.6.0 # via # -r requirements.in + # jupyterhub-ltiauthenticator # mwoauth pymysql==1.0.3 # via -r requirements.in diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index dfdc3a5288..5bcbd44279 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -147,7 +147,7 @@ jupyter-ydoc==0.2.3 # jupyterlab jupyterhub==4.0.0b2 # via -r requirements.in -jupyterlab==3.6.2 +jupyterlab==3.6.3 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert From 410b8a04c5da95e599385685827597fb126ed408 Mon Sep 17 00:00:00 2001 From: Aleksey Karpov <86011874+alekseyolg@users.noreply.github.com> Date: Fri, 31 Mar 2023 19:19:38 +0300 Subject: [PATCH 700/898] Reducing the number of layers in an image small revision of the dockerfile - removed the extra layer --- images/image-awaiter/Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/images/image-awaiter/Dockerfile b/images/image-awaiter/Dockerfile index 2fca06a800..4a0735fd8a 100644 --- a/images/image-awaiter/Dockerfile +++ b/images/image-awaiter/Dockerfile @@ -3,19 +3,19 @@ FROM golang:1.18 # VULN_SCAN_TIME= -RUN mkdir -p /build/ -COPY *.mod *.go *.sum /build/ WORKDIR /build -RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-w -s' -installsuffix cgo -a -o out/image-awaiter +COPY *.mod \ + *.go \ + *.sum \ + . +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-w -s' -installsuffix cgo -a -o out/image-awaiter # present the result within a slimmed image FROM scratch COPY --from=0 /build/out/image-awaiter /image-awaiter - - # To debug / develop this code # ---------------------------- # 1. Setup a kubectl proxy From 76d4e3c5a48565253e6ab36535091d195f2c29be Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 31 Mar 2023 21:34:06 +0100 Subject: [PATCH 701/898] doc: singleuser.uid default is always 1000 --- jupyterhub/schema.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ac4cb8f7c8..fb89332156 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -2343,6 +2343,8 @@ properties: root, enabling it from the container in a startup script, and then transitioning to the normal user. + Default is 1000, set to null to use the container's default. + scheduling: type: object additionalProperties: false From 26d6dfb9216c1f1460a6883266535958e0a36a64 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 3 Apr 2023 10:12:18 +0000 Subject: [PATCH 702/898] Update jupyterhub/configurable-http-proxy version from 4.5.4 to 4.5.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5a3ccb86bf..28e551d630 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -198,7 +198,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.4" # https://github.com/jupyterhub/configurable-http-proxy/tags + tag: "4.5.5" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From b969fa50a20506a1f1a9b9ddb6a719ffde11f0f1 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 3 Apr 2023 10:12:41 +0000 Subject: [PATCH 703/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 1e13576deb..09d204d608 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -174,7 +174,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.7 +sqlalchemy==2.0.8 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 5bcbd44279..2aefd65db1 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -151,7 +151,7 @@ jupyterlab==3.6.3 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.21.0 +jupyterlab-server==2.22.0 # via jupyterlab mako==1.2.4 # via alembic @@ -171,7 +171,7 @@ nbclassic==0.5.4 # -r requirements.in # jupyterlab # notebook -nbclient==0.7.2 +nbclient==0.7.3 # via nbconvert nbconvert==7.2.10 # via @@ -293,7 +293,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4 # via beautifulsoup4 -sqlalchemy==2.0.7 +sqlalchemy==2.0.8 # via # alembic # jupyterhub From d48c45b2e8081e5b380e178c372d5c2bd8feaf94 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 06:23:36 +0000 Subject: [PATCH 704/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eae5aeae23..85a47b0e89 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black args: From 50bf7174b12b4fc90e4e50e6d50830b3cfebb390 Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Tue, 4 Apr 2023 21:09:05 +0530 Subject: [PATCH 705/898] Bump to 3.0.0dev We're already on JupyterHub 4.x, so this makes sense from a semver perspective. Useful in generating better versions in https://hub.jupyter.org/helm-chart/. Credit to @pnasrat for pointing this out. --- chartpress.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chartpress.yaml b/chartpress.yaml index c141c82b95..6a1a973a2a 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,7 +13,7 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- - baseVersion: "2.0.1-0.dev" + baseVersion: "3.0.0-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart From d712573a714e70acb2da09c32e4609a7c9c4c500 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 4 Apr 2023 22:44:05 +0200 Subject: [PATCH 706/898] dependabot: monthly updates of github actions --- .github/dependabot.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 007d3d634e..40e0649746 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -9,8 +9,9 @@ version: 2 updates: # Maintain dependencies in our GitHub Workflows - package-ecosystem: github-actions - directory: "/" # This should be / rather than .github/workflows + directory: / + labels: [ci] schedule: - interval: weekly + interval: monthly time: "05:00" - timezone: "Etc/UTC" + timezone: Etc/UTC From c0d0b3b0d3458e959e655614766df44a29bf1993 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Apr 2023 16:09:03 +0200 Subject: [PATCH 707/898] Comment about how we aim to set baseVersion --- chartpress.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chartpress.yaml b/chartpress.yaml index 6a1a973a2a..8b94b1bada 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -13,6 +13,11 @@ charts: # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. imagePrefix: jupyterhub/k8s- + # baseVersion should be a -0.dev suffixed version, where the version should + # be the next major, minor, or patch version depending on what we have + # merged so far into the main branch. If for example we have merged a + # breaking change it should be the next major version, like 3.0.0-0.dev. + # baseVersion: "3.0.0-0.dev" repo: git: jupyterhub/helm-chart From 7e2acf013dfb92d96696d1c49c23e791e9fb976f Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 7 Apr 2023 05:09:09 +0000 Subject: [PATCH 708/898] Update library/traefik version from v2.9.9 to v2.9.10 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 28e551d630..b0c3415a04 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.9" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.9.10" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From e2440fa47c302a3bb0281f7c9ba96c043f864dcb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 13:56:46 +0000 Subject: [PATCH 709/898] build(deps): bump peter-evans/create-pull-request from 4.2.4 to 5.0.0 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.2.4 to 5.0.0. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/38e0b6e68b4c852a5500a94740f0e535e0d7ba54...5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index ebecbd0c0c..e8fb44396a 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 9347d1846d..212bb02099 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -183,7 +183,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -216,7 +216,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@38e0b6e68b4c852a5500a94740f0e535e0d7ba54 + uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 7d9f0206ca2abac632aa1564ad2314f6ae83af51 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 7 Apr 2023 16:05:18 +0200 Subject: [PATCH 710/898] Use vX instead of hashes for pull request creation action --- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index e8fb44396a..edc2a1240f 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -205,7 +205,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create or update a PR if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request' - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + uses: peter-evans/create-pull-request@v5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 212bb02099..76150636fe 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -114,7 +114,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.tag != steps.latest.outputs.tag - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + uses: peter-evans/create-pull-request@v5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -183,7 +183,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR if: steps.local.outputs.version != steps.latest.outputs.version - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + uses: peter-evans/create-pull-request@v5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> @@ -216,7 +216,7 @@ jobs: # ref: https://github.com/peter-evans/create-pull-request - name: Create a PR - uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5 + uses: peter-evans/create-pull-request@v5 with: token: "${{ secrets.jupyterhub_bot_pat }}" author: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> From 13ac1711c390a94a1b1ae8534632adcbc7cf2f8e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 7 Apr 2023 16:21:35 +0200 Subject: [PATCH 711/898] refactor: rename schema.yaml to values.schema.yaml --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 3 +-- .github/workflows/test-docs.yaml | 4 ++-- docs/Makefile | 2 +- docs/make.bat | 2 +- docs/source/conf.py | 4 ++-- jupyterhub/.helmignore | 2 +- jupyterhub/templates/_helpers-names.tpl | 4 ++-- jupyterhub/{schema.yaml => values.schema.yaml} | 2 +- tools/compare-values-schema-content.py | 12 ++++++------ tools/generate-json-schema.py | 8 ++++---- tools/validate-against-schema.py | 6 +++--- 12 files changed, 25 insertions(+), 26 deletions(-) rename jupyterhub/{schema.yaml => values.schema.yaml} (99%) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 665f3f2503..c746653d0f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -121,7 +121,7 @@ jobs: - name: build chart with chartpress run: | - # Create values.schema.yaml from schema.yaml. + # Create values.schema.json from values.schema.yaml. ./tools/generate-json-schema.py # Append annotations to Chart.yaml with current images so that diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 2304322e5d..7bb57ddfaa 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -252,8 +252,7 @@ jobs: env: DOCKER_BUILDKIT: "1" - # Generate values.schema.json from schema.yaml - - name: Generate values.schema.json from schema.yaml + - name: Generate values.schema.json from values.schema.yaml run: | tools/generate-json-schema.py diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index 8b8bddb44c..b624936259 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -9,12 +9,12 @@ on: pull_request: paths: - "docs/**" - - "**/schema.yaml" + - "**/values.schema.yaml" - "**/test-docs.yaml" push: paths: - "docs/**" - - "**/schema.yaml" + - "**/values.schema.yaml" - "**/test-docs.yaml" branches-ignore: - "dependabot/**" diff --git a/docs/Makefile b/docs/Makefile index 9ef6dac440..44b5069fe6 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -27,7 +27,7 @@ help: # - builds and rebuilds html on changes to source # - starts a livereload enabled webserver and opens up a browser devenv: - sphinx-autobuild -b html --open-browser --ignore "*/reference.md" --watch "../jupyterhub/schema.yaml" "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) + sphinx-autobuild -b html --open-browser --ignore "*/reference.md" --watch "../jupyterhub/values.schema.yaml" "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS) # For local development and CI: # - verifies that links are valid diff --git a/docs/make.bat b/docs/make.bat index d367db957b..0c9af74ba4 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -39,7 +39,7 @@ if errorlevel 9009 ( echo.The 'sphinx-autobuild' command was not found. Open and read README.md! exit /b 1 ) -sphinx-autobuild -b html --open-browser --ignore "*/reference.md" --watch "../jupyterhub/schema.yaml" "%SOURCEDIR%" "%BUILDDIR%/html" %SPHINXOPTS% +sphinx-autobuild -b html --open-browser --ignore "*/reference.md" --watch "../jupyterhub/values.schema.yaml" "%SOURCEDIR%" "%BUILDDIR%/html" %SPHINXOPTS% goto end diff --git a/docs/source/conf.py b/docs/source/conf.py index ebc54a2d96..9a9ed157e0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -89,14 +89,14 @@ def _get_git_ref_from_chartpress_based_version(version): header_md = [ln.strip("\n") for ln in header_md] # schema -with open("../../jupyterhub/schema.yaml") as f: +with open("../../jupyterhub/values.schema.yaml") as f: data = yaml.safe_load(f) def parse_schema(d, md=[], depth=0, pre=""): """ Generate markdown headers from a passed python dictionary created by - parsing a schema.yaml file. + parsing a values.schema.yaml file. """ if "then" in d: d = d["then"] diff --git a/jupyterhub/.helmignore b/jupyterhub/.helmignore index 05f3c9858d..c784d7ca6d 100644 --- a/jupyterhub/.helmignore +++ b/jupyterhub/.helmignore @@ -6,7 +6,7 @@ # # Here are files that we intentionally ignore to avoid them being packaged, # because we don't want to reference them from our templates anyhow. -schema.yaml +values.schema.yaml # Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl index 740e0544f7..f0fe1a51c7 100644 --- a/jupyterhub/templates/_helpers-names.tpl +++ b/jupyterhub/templates/_helpers-names.tpl @@ -3,8 +3,8 @@ parent charts to reference these dynamic resource names. To avoid duplicating documentation, for more information, please see the the - fullnameOverride entry in schema.yaml or the configuration reference that - schema.yaml renders to. + fullnameOverride entry in values.schema.yaml or the configuration reference + that values.schema.yaml renders to. https://z2jh.jupyter.org/en/latest/resources/reference.html#fullnameOverride */}} diff --git a/jupyterhub/schema.yaml b/jupyterhub/values.schema.yaml similarity index 99% rename from jupyterhub/schema.yaml rename to jupyterhub/values.schema.yaml index fb89332156..5812b2159d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -1,6 +1,6 @@ # This schema (a jsonschema in YAML format) is used to generate # values.schema.json which is packaged with the Helm chart for client side -# validation by Helm of values before template rendering. +# validation by helm of values before template rendering. # # This schema is also used by our documentation system to build the # configuration reference section based on the description fields. See diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py index 61e3b754d3..57ba41a721 100755 --- a/tools/compare-values-schema-content.py +++ b/tools/compare-values-schema-content.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 """ -This script is meant to help compare the entries in schema.yaml with the entries -in values.yaml and lint-and-validate-values.yaml. +This script is meant to help compare the entries in values.schema.yaml with the +entries in values.yaml and lint-and-validate-values.yaml. Running this script can result in output like: - schema.yaml entries not found in values.yaml: + values.schema.yaml entries not found in values.yaml: - hub.deploymentStrategy.rollingUpdate - hub.fsGid - rbac.enabled @@ -17,7 +17,7 @@ import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) -schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "schema.yaml") +schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.schema.yaml") values_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.yaml") lint_values_yaml = os.path.join(here_dir, "templates", "lint-and-validate-values.yaml") @@ -96,7 +96,7 @@ def run(): schema_values_diff = get_schema_values_diff(values_yaml, schema, schema_wildcards) if schema_values_diff: - print("schema.yaml entries not found in values.yaml:") + print("values.schema.yaml entries not found in values.yaml:") for l in sorted(schema_values_diff): print(f"- {l}") @@ -104,7 +104,7 @@ def run(): lint_values_yaml, schema, schema_wildcards ) if lint_schema_values_diff: - print("schema.yaml entries not found in lint-and-validate-values.yaml:"), + print("values.schema.yaml entries not found in lint-and-validate-values.yaml:"), for l in sorted(lint_schema_values_diff): print(f"- {l}") diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index f4d6286dcc..5fe87461a1 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ -This script reads schema.yaml and generates a values.schema.json that we can -package with the Helm chart, allowing helm the CLI perform validation. +This script reads values.schema.yaml and generates a values.schema.json that we +can package with the Helm chart, allowing helm the CLI perform validation. -While we can directly generate a values.schema.json from schema.yaml, it +While we can directly generate a values.schema.json from values.schema.yaml, it contains a lot of description text we use to generate our configuration reference that isn't helpful to ship along the validation schema. Due to that, we trim away everything that isn't needed. @@ -16,7 +16,7 @@ import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) -schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "schema.yaml") +schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.schema.yaml") values_schema_json = os.path.join( here_dir, os.pardir, "jupyterhub", "values.schema.json" ) diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index 658f7feee7..83e5918025 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -5,7 +5,7 @@ import yaml here_dir = os.path.abspath(os.path.dirname(__file__)) -schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "schema.yaml") +schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.schema.yaml") values_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "values.yaml") lint_and_validate_values_yaml = os.path.join( here_dir, "templates", "lint-and-validate-values.yaml" @@ -19,12 +19,12 @@ lint_and_validate_values = yaml.safe_load(f) # Validate values.yaml against schema -print("Validating values.yaml against schema.yaml...") +print("Validating values.yaml against values.schema.yaml...") jsonschema.validate(values, schema) print("OK!") print() # Validate lint-and-validate-values.yaml against schema -print("Validating lint-and-validate-values.yaml against schema.yaml...") +print("Validating lint-and-validate-values.yaml against values.schema.yaml...") jsonschema.validate(lint_and_validate_values, schema) print("OK!") From a43f1c26f76dcda92571636f1990c94376a0c84c Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sun, 9 Apr 2023 21:44:22 +0000 Subject: [PATCH 712/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- images/singleuser-sample/requirements.txt | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 09d204d608..1b9ed71190 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.10.2 +alembic==1.10.3 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -124,7 +124,7 @@ pamela==1.0.0 # via jupyterhub prometheus-client==0.16.0 # via jupyterhub -psycopg2==2.9.5 +psycopg2==2.9.6 # via -r requirements.in pyasn1==0.4.8 # via ldap3 @@ -174,7 +174,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.8 +sqlalchemy==2.0.9 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 2aefd65db1..fd20ac186e 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -8,7 +8,7 @@ aiofiles==22.1.0 # via ypy-websocket aiosqlite==0.18.0 # via ypy-websocket -alembic==1.10.2 +alembic==1.10.3 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -31,7 +31,7 @@ babel==2.12.1 # via jupyterlab-server backcall==0.2.0 # via ipython -beautifulsoup4==4.12.0 +beautifulsoup4==4.12.2 # via nbconvert bleach==6.0.0 # via nbconvert @@ -49,7 +49,7 @@ comm==0.1.3 # via ipykernel cryptography==40.0.1 # via pyopenssl -debugpy==1.6.6 +debugpy==1.6.7 # via ipykernel decorator==5.1.1 # via ipython @@ -133,7 +133,7 @@ jupyter-server==2.5.0 # nbclassic # nbgitpuller # notebook-shim -jupyter-server-fileid==0.8.0 +jupyter-server-fileid==0.9.0 # via jupyter-server-ydoc jupyter-server-terminals==0.4.4 # via jupyter-server @@ -166,14 +166,14 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.4 +nbclassic==0.5.5 # via # -r requirements.in # jupyterlab # notebook nbclient==0.7.3 # via nbconvert -nbconvert==7.2.10 +nbconvert==7.3.0 # via # jupyter-server # nbclassic @@ -192,7 +192,7 @@ nest-asyncio==1.5.6 # ipykernel # nbclassic # notebook -notebook==6.5.3 +notebook==6.5.4 # via # jupyterlab # nbgitpuller @@ -293,7 +293,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4 # via beautifulsoup4 -sqlalchemy==2.0.8 +sqlalchemy==2.0.9 # via # alembic # jupyterhub From ab0dc06d01237b049200bc209bca4a044573627f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 11 Apr 2023 21:59:33 +0200 Subject: [PATCH 713/898] Drop support for k8s 1.22 --- .github/workflows/test-chart.yaml | 15 ++++++--------- docs/source/changelog.md | 4 ++-- jupyterhub/Chart.yaml | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 7bb57ddfaa..539d55414d 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -131,16 +131,13 @@ jobs: include: - k3s-channel: latest test: install - - k3s-channel: stable # also test hub-slim + - k3s-channel: stable # also test hub-slim, and prePuller.hook test: install local-chart-extra-args: >- --set hub.image.name=jupyterhub/k8s-hub-slim - - k3s-channel: v1.26 # also test prePuller.hook - test: install - local-chart-extra-args: >- --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - - k3s-channel: v1.25 # also test hub.existingSecret + - k3s-channel: v1.26 # also test hub.existingSecret test: install local-chart-extra-args: >- --set hub.existingSecret=test-hub-existing-secret @@ -153,7 +150,7 @@ jobs: # Helm chart version and then upgrade to the version we are now # testing: # - latest stable version (like 1.2.3) - # - latest dev version (like 1.2.3-n012.h1234abc), + # - latest dev version (like 1.2.3-0.dev.git.5810.hf475e7a4), # - and an old version that requires a JupyterHub DB upgrade # # It can be very useful to see the "Helm diff" step's output from the @@ -163,7 +160,7 @@ jobs: # information from # https://jupyterhub.github.io/helm-chart/info.json # - - k3s-channel: v1.24 + - k3s-channel: v1.25 test: upgrade upgrade-from: stable upgrade-from-extra-args: >- @@ -176,7 +173,7 @@ jobs: --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic create-k8s-test-resources: true - - k3s-channel: v1.23 + - k3s-channel: v1.24 test: upgrade upgrade-from: dev upgrade-from-extra-args: >- @@ -186,7 +183,7 @@ jobs: local-chart-extra-args: >- --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic - - k3s-channel: v1.22 + - k3s-channel: v1.23 test: upgrade # We're testing hub.db.upgrade with PostgreSQL so this version must be old # enough to require a DB upgrade diff --git a/docs/source/changelog.md b/docs/source/changelog.md index c91bf80db3..6c2edb234e 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,9 +12,9 @@ and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. -- K8s 1.22 is now required. +- K8s 1.23 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. -- JupyterHub 3.0.0 is upgraded to 4.0.0b1. +- JupyterHub 3.0.0 is upgraded to 4.0.0b2. - Although it is not officially supported to run a JupyterHub server with a major version different from the singleuser servers' `jupyterhub-singleuser` version, it seems possible during this upgrade. We recommend your singleuser diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 20089f3941..0fc775f301 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -8,7 +8,7 @@ keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg -kubeVersion: ">=1.22.0-0" +kubeVersion: ">=1.23.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers # listed, we have added some below, but in practice the entire JupyterHub team From f35bc2a8850168ad508d6248a7935c6031be6489 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 13 Apr 2023 05:09:02 +0000 Subject: [PATCH 714/898] Update kube-scheduler version from v1.25.8 to v1.25.9 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index b0c3415a04..7b0b5c1ce2 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -505,7 +505,7 @@ scheduling: # workflow, and should be updated there if a minor version bump is done # here. # - tag: "v1.25.8" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.25.9" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md pullPolicy: pullSecrets: [] nodeSelector: {} From 1ddcf69f5720aae6601209bd7fc73197e91e2284 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 17 Apr 2023 15:10:26 +0200 Subject: [PATCH 715/898] docs: fix readme badge for tests --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 56e690a9c0..fe05547b7e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # Zero to JupyterHub with Kubernetes [![Documentation build status](https://img.shields.io/readthedocs/zero-to-jupyterhub?logo=read-the-docs)](https://zero-to-jupyterhub.readthedocs.io/en/latest/?badge=latest) -[![GitHub Workflow Status - Test](https://img.shields.io/github/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/Test%20chart?logo=github&label=tests)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) -[![GitHub Workflow Status - Vuln. scan](https://img.shields.io/github/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/Vuln.%20scan?logo=github&label=Vuln.%20scan)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) +[![GitHub Workflow Status - Test](https://img.shields.io/github/actions/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/test-chart.yaml?logo=github&label=tests)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) +[![GitHub Workflow Status - Vuln. scan](https://img.shields.io/github/actions/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/vuln-scan.yaml?logo=github&label=Vuln.%20scan)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) [![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=stable&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.stable&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#jupyterhub) [![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=pre&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.pre&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) [![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=dev&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.latest&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) From bfc583f8c14ff4a6ab5a5c12d7268bdc3bc2b34a Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 18 Apr 2023 20:33:41 +0000 Subject: [PATCH 716/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 13 ++++++------ images/singleuser-sample/requirements.txt | 24 +++++++++++------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 1b9ed71190..3dcc331b73 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -14,7 +14,7 @@ async-generator==1.10 # via jupyterhub async-timeout==4.0.2 # via aiohttp -attrs==22.2.0 +attrs==23.1.0 # via # aiohttp # jsonschema @@ -34,7 +34,7 @@ charset-normalizer==3.1.0 # via # aiohttp # requests -cryptography==40.0.1 +cryptography==40.0.2 # via # pyjwt # pyopenssl @@ -78,7 +78,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==4.3.0 +jupyterhub-kubespawner==5.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -118,7 +118,7 @@ oauthlib==3.2.2 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator -packaging==23.0 +packaging==23.1 # via jupyterhub pamela==1.0.0 # via jupyterhub @@ -179,13 +179,13 @@ sqlalchemy==2.0.9 # alembic # jupyterhub # sqlalchemy-cockroachdb -sqlalchemy-cockroachdb==2.0.0 +sqlalchemy-cockroachdb==2.0.1 # via -r requirements.in statsd==4.0.1 # via -r requirements.in text-unidecode==1.3 # via python-slugify -tornado==6.2 +tornado==6.3 # via # jupyterhub # jupyterhub-idle-culler @@ -194,6 +194,7 @@ traitlets==5.9.0 # via # jupyter-telemetry # jupyterhub + # jupyterhub-kubespawner # jupyterhub-ldapauthenticator typing-extensions==4.5.0 # via diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index fd20ac186e..d461d08468 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -6,7 +6,7 @@ # aiofiles==22.1.0 # via ypy-websocket -aiosqlite==0.18.0 +aiosqlite==0.19.0 # via ypy-websocket alembic==1.10.3 # via jupyterhub @@ -25,7 +25,7 @@ asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub -attrs==22.2.0 +attrs==23.1.0 # via jsonschema babel==2.12.1 # via jupyterlab-server @@ -47,7 +47,7 @@ charset-normalizer==3.1.0 # via requests comm==0.1.3 # via ipykernel -cryptography==40.0.1 +cryptography==40.0.2 # via pyopenssl debugpy==1.6.7 # via ipykernel @@ -103,7 +103,7 @@ jsonschema[format-nongpl]==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==8.1.0 +jupyter-client==8.2.0 # via # ipykernel # jupyter-server @@ -141,7 +141,7 @@ jupyter-server-ydoc==0.8.0 # via jupyterlab jupyter-telemetry==0.1.0 # via jupyterhub -jupyter-ydoc==0.2.3 +jupyter-ydoc==0.2.4 # via # jupyter-server-ydoc # jupyterlab @@ -151,7 +151,7 @@ jupyterlab==3.6.3 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.22.0 +jupyterlab-server==2.22.1 # via jupyterlab mako==1.2.4 # via alembic @@ -173,7 +173,7 @@ nbclassic==0.5.5 # notebook nbclient==0.7.3 # via nbconvert -nbconvert==7.3.0 +nbconvert==7.3.1 # via # jupyter-server # nbclassic @@ -200,7 +200,7 @@ notebook-shim==0.2.2 # via nbclassic oauthlib==3.2.2 # via jupyterhub -packaging==23.0 +packaging==23.1 # via # ipykernel # jupyter-server @@ -228,7 +228,7 @@ prometheus-client==0.16.0 # notebook prompt-toolkit==3.0.38 # via ipython -psutil==5.9.4 +psutil==5.9.5 # via ipykernel ptyprocess==0.7.0 # via @@ -238,7 +238,7 @@ pure-eval==0.2.2 # via stack-data pycparser==2.21 # via cffi -pygments==2.14.0 +pygments==2.15.1 # via # ipython # nbconvert @@ -291,7 +291,7 @@ six==1.16.0 # rfc3339-validator sniffio==1.3.0 # via anyio -soupsieve==2.4 +soupsieve==2.4.1 # via beautifulsoup4 sqlalchemy==2.0.9 # via @@ -307,7 +307,7 @@ terminado==0.17.1 # notebook tinycss2==1.2.1 # via nbconvert -tornado==6.2 +tornado==6.3 # via # ipykernel # jupyter-client From efdec6ecd9b515de69264c708c8342b5cb1904cc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 18 Apr 2023 23:12:13 +0200 Subject: [PATCH 717/898] Add changelog entry about KubeSpawner 5 --- docs/source/changelog.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6c2edb234e..313631e20f 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -21,7 +21,16 @@ this list should be updated. images are upgraded as soon as practical, before or after the JupyterHub chart is upgraded. We expect running user servers to be able to keep running during the upgrade. - - Changelog available at https://jupyterhub.readthedocs.io/en/stable/changelog.html + - Please refer to the [JupyterHub changelog] for details +- KubeSpawner 4.2.0 is upgraded to 5.0.0 + - Please read to the [KubeSpawner changelog]'s breaking changes and be aware + that configuring [`singleuser.extraEnv`](schema_singleuser.extraEnv) is to + configure `KubeSpawner.environment`, and to configure + [`singleuser.profileList`](schema_singleuser.profileList) is to configure + `KubeSpawner.profile_list`. + +[jupyterhub changelog]: https://jupyterhub.readthedocs.io/en/stable/changelog.html +[kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html ## 2.0 From 50fe018a07fbe46da5db62ad754f698c948b3e78 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 21 Apr 2023 01:32:12 +0000 Subject: [PATCH 718/898] Update jupyterhub from 4.0.0b2 to 4.0.0 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index a368bb2213..de0bab8cd8 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0b2 +jupyterhub==4.0.0 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3dcc331b73..add4415347 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -62,7 +62,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.0b2 +jupyterhub==4.0.0 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -126,7 +126,7 @@ prometheus-client==0.16.0 # via jupyterhub psycopg2==2.9.6 # via -r requirements.in -pyasn1==0.4.8 +pyasn1==0.5.0 # via ldap3 pycparser==2.21 # via cffi diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 13c07cc03f..1fc17a3652 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0b2 +jupyterhub==4.0.0 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index d461d08468..a370ca5f4b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -145,7 +145,7 @@ jupyter-ydoc==0.2.4 # via # jupyter-server-ydoc # jupyterlab -jupyterhub==4.0.0b2 +jupyterhub==4.0.0 # via -r requirements.in jupyterlab==3.6.3 # via -r requirements.in diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 0fc775f301..73ec860307 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "4.0.0b2" +appVersion: "4.0.0" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 624f3adbc4f24e9f2eed7a12d0b86f9bb8db1e96 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 21 Apr 2023 03:41:19 +0200 Subject: [PATCH 719/898] Update changelog about jupyterhub 4 --- docs/source/changelog.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 313631e20f..7c33b46bb6 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,14 +14,11 @@ this list should be updated. - K8s 1.23 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. -- JupyterHub 3.0.0 is upgraded to 4.0.0b2. - - Although it is not officially supported to run a JupyterHub server with a - major version different from the singleuser servers' `jupyterhub-singleuser` - version, it seems possible during this upgrade. We recommend your singleuser - images are upgraded as soon as practical, before or after the JupyterHub - chart is upgraded. We expect running user servers to be able to keep running - during the upgrade. - - Please refer to the [JupyterHub changelog] for details +- JupyterHub 3.0.0 is upgraded to 4.0.0. + - Please refer to the [JupyterHub changelog] for details, but note that this + upgrade won't require user servers to be restarted or first install version + 4 of `jupyterhub` (PyPI) or `jupyterhub-base` (conda-forge) in their user + environments. - KubeSpawner 4.2.0 is upgraded to 5.0.0 - Please read to the [KubeSpawner changelog]'s breaking changes and be aware that configuring [`singleuser.extraEnv`](schema_singleuser.extraEnv) is to From 796c59b6bd164862f9049e080808534a80318c3a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 25 Apr 2023 05:09:26 +0000 Subject: [PATCH 720/898] Update library/traefik version from v2.9.10 to v2.10.0 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 7b0b5c1ce2..3b0619cc28 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.9.10" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.10.0" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 9410d6c1c3ccb40d6b30cbd9a66b249e232b5af4 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 28 Apr 2023 05:09:10 +0000 Subject: [PATCH 721/898] Update library/traefik version from v2.10.0 to v2.10.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 3b0619cc28..f17bcb0eeb 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.10.0" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.10.1" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 246f1f0c7171b12a27fcd377e5172a9b95f3e215 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 28 Apr 2023 11:50:11 +0000 Subject: [PATCH 722/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 10 +++++----- images/singleuser-sample/requirements.txt | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index add4415347..bc1a2057c7 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.10.3 +alembic==1.10.4 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -156,7 +156,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.28.2 +requests==2.29.0 # via # jupyterhub # mwoauth @@ -174,7 +174,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.9 +sqlalchemy==2.0.11 # via # alembic # jupyterhub @@ -185,7 +185,7 @@ statsd==4.0.1 # via -r requirements.in text-unidecode==1.3 # via python-slugify -tornado==6.3 +tornado==6.3.1 # via # jupyterhub # jupyterhub-idle-culler @@ -205,7 +205,7 @@ urllib3==1.26.15 # jupyterhub-kubespawner # kubernetes-asyncio # requests -yarl==1.8.2 +yarl==1.9.2 # via aiohttp # The following packages are considered to be unsafe in a requirements file: diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index a370ca5f4b..d55786f59d 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -8,7 +8,7 @@ aiofiles==22.1.0 # via ypy-websocket aiosqlite==0.19.0 # via ypy-websocket -alembic==1.10.3 +alembic==1.10.4 # via jupyterhub anyio==3.6.2 # via jupyter-server @@ -72,7 +72,7 @@ ipykernel==6.22.0 # via # nbclassic # notebook -ipython==8.12.0 +ipython==8.12.1 # via # ipykernel # jupyterlab @@ -166,12 +166,12 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.5 +nbclassic==0.5.6 # via # -r requirements.in # jupyterlab # notebook -nbclient==0.7.3 +nbclient==0.7.4 # via nbconvert nbconvert==7.3.1 # via @@ -196,7 +196,7 @@ notebook==6.5.4 # via # jupyterlab # nbgitpuller -notebook-shim==0.2.2 +notebook-shim==0.2.3 # via nbclassic oauthlib==3.2.2 # via jupyterhub @@ -218,7 +218,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.2.0 +platformdirs==3.5.0 # via jupyter-core prometheus-client==0.16.0 # via @@ -264,7 +264,7 @@ pyzmq==25.0.2 # jupyter-server # nbclassic # notebook -requests==2.28.2 +requests==2.29.0 # via # jupyterhub # jupyterlab-server @@ -278,7 +278,7 @@ rfc3986-validator==0.1.1 # jupyter-events ruamel-yaml==0.17.21 # via jupyter-telemetry -send2trash==1.8.0 +send2trash==1.8.2 # via # jupyter-server # nbclassic @@ -293,7 +293,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.9 +sqlalchemy==2.0.11 # via # alembic # jupyterhub @@ -307,7 +307,7 @@ terminado==0.17.1 # notebook tinycss2==1.2.1 # via nbconvert -tornado==6.3 +tornado==6.3.1 # via # ipykernel # jupyter-client From 879d2e0eeba1de260b710d89d612082181597756 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 1 May 2023 05:14:00 +0000 Subject: [PATCH 723/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 0ddc0c230e..ac4ade8cc1 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-02-27_05:15:25 +# VULN_SCAN_TIME=2023-05-01_05:13:58 # The build stage From bbee697fb9a0483d8d10137cec065ee7c94eb53b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 05:56:36 +0000 Subject: [PATCH 724/898] build(deps): bump aquasecurity/trivy-action from 0.9.2 to 0.10.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.9.2 to 0.10.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/1f0aa582c8c8f5f7639610d6d38baddfea4fdcee...e5f43133f6e8736992c9f3c1b3296e24b37e17f2) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index edc2a1240f..caacc6fa94 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee + uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee + uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee + uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 with: image-ref: rebuilt-image format: table From e0e1c71564d36bff857b657c0d8f97a1eb3d84f0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 04:28:33 +0000 Subject: [PATCH 725/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.3.1 → v3.3.2](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.3.2) - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.6 → v3.0.0-alpha.9-for-vscode](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.6...v3.0.0-alpha.9-for-vscode) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 85a47b0e89..6ba593a05c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.3.2 hooks: - id: pyupgrade args: @@ -54,7 +54,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.6 + rev: v3.0.0-alpha.9-for-vscode hooks: - id: prettier From beca3ffdc85d2fd1a0563c3f9b319eec9eb3850c Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 8 May 2023 05:12:55 +0000 Subject: [PATCH 726/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 6056cea343..9d147a91b4 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-03-30_17:38:57 +# VULN_SCAN_TIME=2023-05-08_05:12:53 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 98fa8707c7839f29883723dfa944876ab3c9eb43 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 8 May 2023 05:14:27 +0000 Subject: [PATCH 727/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 58a1d273d5..cf0858df5b 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-02-27_05:15:39 +# VULN_SCAN_TIME=2023-05-08_05:14:25 # The build stage From 1a3ddda873ad35e3b34952057754112ac7be516d Mon Sep 17 00:00:00 2001 From: Junaid Chaudry Date: Mon, 1 May 2023 14:01:31 -0400 Subject: [PATCH 728/898] Adding support for spawner 'k8s_api_request_timeout' parameter override --- jupyterhub/values.schema.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 5812b2159d..1aba68a573 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -247,6 +247,22 @@ properties: - jovyan2 ``` + __Example__ + + If your kubernetes pods take longer than usual to spawn, the users notebooks + may fail to spawn with an error like `'s server failed to start in 30000 + seconds, giving up`. In such scenarios, you may need to adjust + [KubeSpawner defaults](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.k8s_api_request_timeout) + which can also be done using `hub.config`. For example, + + ```yaml + hub: + config: + KubeSpawner: + k8s_api_request_timeout: 3600 + ... + ``` + ```{admonition} YAML limitations :class: tip You can't represent Python `Bytes` or `Set` objects in YAML directly. From fe77a093afe040b3c6ef87c1baae5aae34919fc5 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 12 May 2023 01:14:16 +0200 Subject: [PATCH 729/898] Update kube-scheduler in user-scheduler from 1.25.9 to 1.26.4 --- .github/workflows/watch-dependencies.yaml | 2 +- jupyterhub/templates/scheduling/user-scheduler/rbac.yaml | 8 ++++---- jupyterhub/values.yaml | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index 76150636fe..efd2f88d47 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -68,7 +68,7 @@ jobs: registry: registry.k8s.io repository: kube-scheduler values_path: scheduling.userScheduler.image.tag - version_startswith: "v1.25" + version_startswith: "v1.26" version_patch_regexp_group_suffix: "" - name: pause diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml index 4acabd8b81..7e188c742d 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -19,9 +19,9 @@ rules: # - unchanged between 1.18 and 1.20 # - changed in 1.21: get/list/watch permission for namespace, # csidrivers, csistoragecapacities was added. - # - unchanged between 1.22 and 1.26 + # - unchanged between 1.22 and 1.27 # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.26.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L730-L886 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.27.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L736-L892 - apiGroups: - "" - events.k8s.io @@ -183,9 +183,9 @@ rules: # Copied from the system:volume-scheduler ClusterRole of the k8s version # matching the kube-scheduler binary we use. # - # NOTE: These rules have not changed between 1.12 and 1.26. + # NOTE: These rules have not changed between 1.12 and 1.27. # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.26.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1306-L1333 + # ref: https://github.com/kubernetes/kubernetes/blob/v1.27.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1311-L1338 - apiGroups: - "" resources: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index f17bcb0eeb..25f48a8c16 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -503,9 +503,10 @@ scheduling: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. The minor version is pinned in the # workflow, and should be updated there if a minor version bump is done - # here. + # here. We aim to stay around 1 minor version behind the latest k8s + # version. # - tag: "v1.25.9" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + tag: "v1.26.4" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 2e0decfa1319b331848f5da13ee9f26aa0256b99 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 13 May 2023 18:31:59 +0000 Subject: [PATCH 730/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 16 +++++++++------- images/singleuser-sample/requirements.txt | 22 ++++++++++++---------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index bc1a2057c7..eda71b31a7 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -22,7 +22,7 @@ bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2022.12.7 +certifi==2023.5.7 # via # kubernetes-asyncio # requests @@ -88,7 +88,7 @@ jupyterhub-nativeauthenticator==1.1.0 # via -r requirements.in jupyterhub-tmpauthenticator==0.6 # via -r requirements.in -kubernetes-asyncio==24.2.2 +kubernetes-asyncio==24.2.3 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -132,7 +132,7 @@ pycparser==2.21 # via cffi pycurl==7.45.2 # via -r requirements.in -pyjwt[crypto]==2.6.0 +pyjwt[crypto]==2.7.0 # via # -r requirements.in # jupyterhub-ltiauthenticator @@ -156,7 +156,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.29.0 +requests==2.30.0 # via # jupyterhub # mwoauth @@ -164,17 +164,19 @@ requests==2.29.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -ruamel-yaml==0.17.21 +ruamel-yaml==0.17.26 # via # jupyter-telemetry # oauthenticator +ruamel-yaml-clib==0.2.7 + # via ruamel-yaml six==1.16.0 # via # kubernetes-asyncio # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.11 +sqlalchemy==2.0.13 # via # alembic # jupyterhub @@ -200,7 +202,7 @@ typing-extensions==4.5.0 # via # alembic # sqlalchemy -urllib3==1.26.15 +urllib3==2.0.2 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index d55786f59d..ca8c3e5fd9 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -35,7 +35,7 @@ beautifulsoup4==4.12.2 # via nbconvert bleach==6.0.0 # via nbconvert -certifi==2022.12.7 +certifi==2023.5.7 # via requests certipy==0.1.3 # via jupyterhub @@ -68,11 +68,11 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.22.0 +ipykernel==6.23.0 # via # nbclassic # notebook -ipython==8.12.1 +ipython==8.13.2 # via # ipykernel # jupyterlab @@ -166,14 +166,14 @@ matplotlib-inline==0.1.6 # ipython mistune==2.0.5 # via nbconvert -nbclassic==0.5.6 +nbclassic==1.0.0 # via # -r requirements.in # jupyterlab # notebook nbclient==0.7.4 # via nbconvert -nbconvert==7.3.1 +nbconvert==7.4.0 # via # jupyter-server # nbclassic @@ -218,7 +218,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.5.0 +platformdirs==3.5.1 # via jupyter-core prometheus-client==0.16.0 # via @@ -264,7 +264,7 @@ pyzmq==25.0.2 # jupyter-server # nbclassic # notebook -requests==2.29.0 +requests==2.30.0 # via # jupyterhub # jupyterlab-server @@ -276,8 +276,10 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -ruamel-yaml==0.17.21 +ruamel-yaml==0.17.26 # via jupyter-telemetry +ruamel-yaml-clib==0.2.7 + # via ruamel-yaml send2trash==1.8.2 # via # jupyter-server @@ -293,7 +295,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.11 +sqlalchemy==2.0.13 # via # alembic # jupyterhub @@ -341,7 +343,7 @@ typing-extensions==4.5.0 # sqlalchemy uri-template==1.2.0 # via jsonschema -urllib3==1.26.15 +urllib3==2.0.2 # via requests wcwidth==0.2.6 # via prompt-toolkit From 7e7b0c5e1051725e712246de757534437083f684 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 18 May 2023 05:08:55 +0000 Subject: [PATCH 731/898] Update kube-scheduler version from v1.26.4 to v1.26.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 25f48a8c16..96dd9bb714 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -506,7 +506,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.4" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.5" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 50a08fad212829bd21865b65888e35d012938882 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 22 May 2023 05:12:46 +0000 Subject: [PATCH 732/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 9239486b64..7002d03ae5 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2023-03-30_17:38:28 +# VULN_SCAN_TIME=2023-05-22_05:12:45 RUN apk add --no-cache iptables From 4e54d27033866239b290f73e5c7a67bc29b0a64e Mon Sep 17 00:00:00 2001 From: Christopher Bowman Date: Mon, 22 May 2023 15:27:13 -0400 Subject: [PATCH 733/898] Update installation.md Remove double word cluster --- docs/source/jupyterhub/installation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index e61eabfc34..cc89443a21 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -2,9 +2,9 @@ # Installing JupyterHub -With a {doc}`Kubernetes cluster ` cluster -available and {doc}`Helm ` installed, we can install -JupyterHub in the Kubernetes cluster using the JupyterHub Helm chart. +With a {doc}`Kubernetes cluster ` available +and {doc}`Helm ` installed, we can install JupyterHub +in the Kubernetes cluster using the JupyterHub Helm chart. ## Initialize a Helm chart configuration file From 4c599336c6819c8c27d83a239d3b39af73d1f94b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 07:12:53 +0000 Subject: [PATCH 734/898] build(deps): bump requests from 2.30.0 to 2.31.0 in /images/hub Bumps [requests](https://github.com/psf/requests) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index eda71b31a7..2699f24671 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -156,7 +156,7 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio -requests==2.30.0 +requests==2.31.0 # via # jupyterhub # mwoauth From bcac5a6738f1b42c8c899d961583ed8a299d0e34 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 08:00:34 +0000 Subject: [PATCH 735/898] build(deps): bump requests in /images/singleuser-sample Bumps [requests](https://github.com/psf/requests) from 2.30.0 to 2.31.0. - [Release notes](https://github.com/psf/requests/releases) - [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md) - [Commits](https://github.com/psf/requests/compare/v2.30.0...v2.31.0) --- updated-dependencies: - dependency-name: requests dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index ca8c3e5fd9..9698a03713 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -264,7 +264,7 @@ pyzmq==25.0.2 # jupyter-server # nbclassic # notebook -requests==2.30.0 +requests==2.31.0 # via # jupyterhub # jupyterlab-server From e533da7633826706ddfb6d3cab9c5f518e3caeaf Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 23 May 2023 17:37:53 +0100 Subject: [PATCH 736/898] Helm chart url has changed Relying on clients to follow a redirect may not be robust. https://discourse.jupyter.org/t/helm-chart-certificate-x509-error-when-deploying-on-eks-using-aws-cdk/19524 --- docs/source/jupyterhub/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index e61eabfc34..ea9d246e0d 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -44,11 +44,11 @@ can try with `nano config.yaml`. ## Install JupyterHub -1. Make Helm aware of the [JupyterHub Helm chart repository](https://jupyterhub.github.io/helm-chart/) so you can install the +1. Make Helm aware of the [JupyterHub Helm chart repository](https://hub.jupyter.org/helm-chart/) so you can install the JupyterHub chart from it without having to use a long URL name. ``` - helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/ + helm repo add jupyterhub https://hub.jupyter.org/helm-chart/ helm repo update ``` From 43af1c5548ed0df56b55980a67de442931af0337 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 18:58:58 +0000 Subject: [PATCH 737/898] build(deps): bump tornado from 6.3.1 to 6.3.2 in /images/hub Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.3.1 to 6.3.2. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.3.1...v6.3.2) --- updated-dependencies: - dependency-name: tornado dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 2699f24671..fd3ec5e836 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -187,7 +187,7 @@ statsd==4.0.1 # via -r requirements.in text-unidecode==1.3 # via python-slugify -tornado==6.3.1 +tornado==6.3.2 # via # jupyterhub # jupyterhub-idle-culler From a69d8ec60cbc5bde1fac5ada2c3eac44c1b5309e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 May 2023 19:00:11 +0000 Subject: [PATCH 738/898] build(deps): bump tornado in /images/singleuser-sample Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.3.1 to 6.3.2. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.3.1...v6.3.2) --- updated-dependencies: - dependency-name: tornado dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 9698a03713..bf5777912a 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -309,7 +309,7 @@ terminado==0.17.1 # notebook tinycss2==1.2.1 # via nbconvert -tornado==6.3.1 +tornado==6.3.2 # via # ipykernel # jupyter-client From 0f4b2d0b4dde8c9e04833c8c35921086f3bc3ac7 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 29 May 2023 05:12:49 +0000 Subject: [PATCH 739/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 9d147a91b4..3256d54059 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-05-08_05:12:53 +# VULN_SCAN_TIME=2023-05-29_05:12:47 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From f9af31a37a0fb9af39e4f23ff8d57f68db96a107 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 31 May 2023 09:18:25 +0000 Subject: [PATCH 740/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 22 +++---- images/singleuser-sample/requirements.txt | 76 +++++++++-------------- 2 files changed, 42 insertions(+), 56 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index fd3ec5e836..65ad81c510 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.4 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.10.4 +alembic==1.11.1 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -34,7 +34,7 @@ charset-normalizer==3.1.0 # via # aiohttp # requests -cryptography==40.0.2 +cryptography==41.0.0 # via # pyjwt # pyopenssl @@ -78,15 +78,15 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==5.0.0 +jupyterhub-kubespawner==6.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.5.0 +jupyterhub-ltiauthenticator==1.5.1 # via -r requirements.in -jupyterhub-nativeauthenticator==1.1.0 +jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in -jupyterhub-tmpauthenticator==0.6 +jupyterhub-tmpauthenticator==1.0.0 # via -r requirements.in kubernetes-asyncio==24.2.3 # via jupyterhub-kubespawner @@ -122,7 +122,7 @@ packaging==23.1 # via jupyterhub pamela==1.0.0 # via jupyterhub -prometheus-client==0.16.0 +prometheus-client==0.17.0 # via jupyterhub psycopg2==2.9.6 # via -r requirements.in @@ -139,7 +139,7 @@ pyjwt[crypto]==2.7.0 # mwoauth pymysql==1.0.3 # via -r requirements.in -pyopenssl==23.1.1 +pyopenssl==23.2.0 # via certipy pyrsistent==0.19.3 # via jsonschema @@ -164,7 +164,7 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -ruamel-yaml==0.17.26 +ruamel-yaml==0.17.31 # via # jupyter-telemetry # oauthenticator @@ -176,7 +176,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.13 +sqlalchemy==2.0.15 # via # alembic # jupyterhub @@ -198,7 +198,7 @@ traitlets==5.9.0 # jupyterhub # jupyterhub-kubespawner # jupyterhub-ldapauthenticator -typing-extensions==4.5.0 +typing-extensions==4.6.2 # via # alembic # sqlalchemy diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index bf5777912a..7e0eb027a0 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,13 +4,9 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiofiles==22.1.0 - # via ypy-websocket -aiosqlite==0.19.0 - # via ypy-websocket -alembic==1.10.4 +alembic==1.11.1 # via jupyterhub -anyio==3.6.2 +anyio==3.7.0 # via jupyter-server argon2-cffi==21.3.0 # via @@ -25,6 +21,8 @@ asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub +async-lru==2.0.2 + # via jupyterlab attrs==23.1.0 # via jsonschema babel==2.12.1 @@ -47,7 +45,7 @@ charset-normalizer==3.1.0 # via requests comm==0.1.3 # via ipykernel -cryptography==40.0.2 +cryptography==41.0.0 # via pyopenssl debugpy==1.6.7 # via ipykernel @@ -57,7 +55,7 @@ defusedxml==0.7.1 # via nbconvert executing==1.2.0 # via stack-data -fastjsonschema==2.16.3 +fastjsonschema==2.17.1 # via nbformat fqdn==1.5.1 # via jsonschema @@ -68,14 +66,13 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.23.0 +ipykernel==6.23.1 # via + # jupyterlab # nbclassic # notebook ipython==8.13.2 - # via - # ipykernel - # jupyterlab + # via ipykernel ipython-genutils==0.2.0 # via # nbclassic @@ -93,7 +90,7 @@ jinja2==3.1.2 # nbclassic # nbconvert # notebook -json5==0.9.11 +json5==0.9.14 # via jupyterlab-server jsonpointer==2.3 # via jsonschema @@ -122,32 +119,24 @@ jupyter-core==5.3.0 # nbformat # notebook jupyter-events==0.6.3 + # via jupyter-server +jupyter-lsp==2.2.0 + # via jupyterlab +jupyter-server==2.6.0 # via - # jupyter-server - # jupyter-server-fileid -jupyter-server==2.5.0 - # via - # jupyter-server-fileid + # jupyter-lsp # jupyterlab # jupyterlab-server # nbclassic # nbgitpuller # notebook-shim -jupyter-server-fileid==0.9.0 - # via jupyter-server-ydoc jupyter-server-terminals==0.4.4 # via jupyter-server -jupyter-server-ydoc==0.8.0 - # via jupyterlab jupyter-telemetry==0.1.0 # via jupyterhub -jupyter-ydoc==0.2.4 - # via - # jupyter-server-ydoc - # jupyterlab jupyterhub==4.0.0 # via -r requirements.in -jupyterlab==3.6.3 +jupyterlab==4.0.1 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert @@ -169,9 +158,8 @@ mistune==2.0.5 nbclassic==1.0.0 # via # -r requirements.in - # jupyterlab # notebook -nbclient==0.7.4 +nbclient==0.8.0 # via nbconvert nbconvert==7.4.0 # via @@ -193,13 +181,15 @@ nest-asyncio==1.5.6 # nbclassic # notebook notebook==6.5.4 + # via nbgitpuller +notebook-shim==0.2.3 # via # jupyterlab - # nbgitpuller -notebook-shim==0.2.3 - # via nbclassic + # nbclassic oauthlib==3.2.2 # via jupyterhub +overrides==7.3.1 + # via jupyter-server packaging==23.1 # via # ipykernel @@ -220,7 +210,7 @@ pickleshare==0.7.5 # via ipython platformdirs==3.5.1 # via jupyter-core -prometheus-client==0.16.0 +prometheus-client==0.17.0 # via # jupyter-server # jupyterhub @@ -242,7 +232,7 @@ pygments==2.15.1 # via # ipython # nbconvert -pyopenssl==23.1.1 +pyopenssl==23.2.0 # via certipy pyrsistent==0.19.3 # via jsonschema @@ -257,7 +247,7 @@ python-json-logger==2.0.7 # jupyter-telemetry pyyaml==6.0 # via jupyter-events -pyzmq==25.0.2 +pyzmq==25.1.0 # via # ipykernel # jupyter-client @@ -276,7 +266,7 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -ruamel-yaml==0.17.26 +ruamel-yaml==0.17.31 # via jupyter-telemetry ruamel-yaml-clib==0.2.7 # via ruamel-yaml @@ -295,7 +285,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.13 +sqlalchemy==2.0.15 # via # alembic # jupyterhub @@ -331,15 +321,17 @@ traitlets==5.9.0 # jupyter-server # jupyter-telemetry # jupyterhub + # jupyterlab # matplotlib-inline # nbclassic # nbclient # nbconvert # nbformat # notebook -typing-extensions==4.5.0 +typing-extensions==4.6.2 # via # alembic + # async-lru # sqlalchemy uri-template==1.2.0 # via jsonschema @@ -353,11 +345,5 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.1 +websocket-client==1.5.2 # via jupyter-server -y-py==0.5.9 - # via - # jupyter-ydoc - # ypy-websocket -ypy-websocket==0.8.2 - # via jupyter-server-ydoc From 8c7e877f532e7ca8f3be98bd8c77da039900f9e4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 2 Jun 2023 18:55:34 +0200 Subject: [PATCH 741/898] Reduce example complexity --- jupyterhub/values.schema.yaml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 1aba68a573..51324ebeb9 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -233,6 +233,8 @@ properties: ```python c.JupyterHub.admin_access = true c.JupyterHub.admin_users = ["jovyan1", "jovyan2"] + c.KubeSpawner.k8s_api_request_timeout = 10 + c.GitHubOAuthenticator.allowed_organizations = ["jupyterhub"] ``` Then, you would be able to represent it with this configuration like: @@ -245,22 +247,11 @@ properties: admin_users: - jovyan1 - jovyan2 - ``` - - __Example__ - - If your kubernetes pods take longer than usual to spawn, the users notebooks - may fail to spawn with an error like `'s server failed to start in 30000 - seconds, giving up`. In such scenarios, you may need to adjust - [KubeSpawner defaults](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner.k8s_api_request_timeout) - which can also be done using `hub.config`. For example, - - ```yaml - hub: - config: - KubeSpawner: - k8s_api_request_timeout: 3600 - ... + KubeSpawner: + k8s_api_request_timeout: 10 + GitHubOAuthenticator: + allowed_organizations: + - jupyterhub ``` ```{admonition} YAML limitations From 529ee9d0e181310c261079f9bbe36455d8e6136b Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:52:12 +0000 Subject: [PATCH 742/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index ac4ade8cc1..e4d9a4ccae 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-05-01_05:13:58 +# VULN_SCAN_TIME=2023-06-04_17:52:10 # The build stage From bb9bde6f8eda41947603ab427fe627b755d936ef Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:52:13 +0000 Subject: [PATCH 743/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index cf0858df5b..de0a65492f 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-05-08_05:14:25 +# VULN_SCAN_TIME=2023-06-04_17:52:11 # The build stage From 504b36d8ff6667fc29b50ec1ce6c7bc79720b291 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:53:30 +0000 Subject: [PATCH 744/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 8 ++++---- images/singleuser-sample/requirements.txt | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 65ad81c510..46f7884926 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -34,7 +34,7 @@ charset-normalizer==3.1.0 # via # aiohttp # requests -cryptography==41.0.0 +cryptography==41.0.1 # via # pyjwt # pyopenssl @@ -94,7 +94,7 @@ ldap3==2.9.1 # via jupyterhub-ldapauthenticator mako==1.2.4 # via alembic -markupsafe==2.1.2 +markupsafe==2.1.3 # via # jinja2 # mako @@ -120,7 +120,7 @@ onetimepass==1.0.1 # via jupyterhub-nativeauthenticator packaging==23.1 # via jupyterhub -pamela==1.0.0 +pamela==1.1.0 # via jupyterhub prometheus-client==0.17.0 # via jupyterhub @@ -198,7 +198,7 @@ traitlets==5.9.0 # jupyterhub # jupyterhub-kubespawner # jupyterhub-ldapauthenticator -typing-extensions==4.6.2 +typing-extensions==4.6.3 # via # alembic # sqlalchemy diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 7e0eb027a0..03288c91d0 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -45,7 +45,7 @@ charset-normalizer==3.1.0 # via requests comm==0.1.3 # via ipykernel -cryptography==41.0.0 +cryptography==41.0.1 # via pyopenssl debugpy==1.6.7 # via ipykernel @@ -71,7 +71,7 @@ ipykernel==6.23.1 # jupyterlab # nbclassic # notebook -ipython==8.13.2 +ipython==8.14.0 # via ipykernel ipython-genutils==0.2.0 # via @@ -144,7 +144,7 @@ jupyterlab-server==2.22.1 # via jupyterlab mako==1.2.4 # via alembic -markupsafe==2.1.2 +markupsafe==2.1.3 # via # jinja2 # mako @@ -166,7 +166,7 @@ nbconvert==7.4.0 # jupyter-server # nbclassic # notebook -nbformat==5.8.0 +nbformat==5.9.0 # via # jupyter-server # nbclassic @@ -198,7 +198,7 @@ packaging==23.1 # jupyterlab # jupyterlab-server # nbconvert -pamela==1.0.0 +pamela==1.1.0 # via jupyterhub pandocfilters==1.5.0 # via nbconvert @@ -328,7 +328,7 @@ traitlets==5.9.0 # nbconvert # nbformat # notebook -typing-extensions==4.6.2 +typing-extensions==4.6.3 # via # alembic # async-lru From 409b95ce29fc87697e7b05b2511f3cd05dee8e70 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 12 May 2023 00:55:13 +0200 Subject: [PATCH 745/898] Add changelog for 3.0.0-alpha.1 --- docs/source/changelog.md | 111 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 7c33b46bb6..8d4981c480 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,6 +12,16 @@ and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. +## 3.0 + +### 3.0.0-alpha.1 - 2023-05-12 + +This is an alpha release as additional breaking changes are still planned, see +[this issue](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/3091) +for details. + +#### Breaking changes + - K8s 1.23 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. - JupyterHub 3.0.0 is upgraded to 4.0.0. @@ -19,15 +29,114 @@ this list should be updated. upgrade won't require user servers to be restarted or first install version 4 of `jupyterhub` (PyPI) or `jupyterhub-base` (conda-forge) in their user environments. -- KubeSpawner 4.2.0 is upgraded to 5.0.0 +- KubeSpawner 4.2.0 is upgraded to 6.0.0 - Please read to the [KubeSpawner changelog]'s breaking changes and be aware that configuring [`singleuser.extraEnv`](schema_singleuser.extraEnv) is to configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. +- OAuthenticator 15.1.0 _will be_ upgraded to 16.0.0, _but isn't yet in the alpha.1 release_ + - If you are using an JupyterHub Authenticator class from this package, please + read to the [OAuthenticator changelog]'s breaking changes. +- TmpAuthenticator 0.6 is upgraded to 1.0.0 + - If you are using this JupyterHub Authenticator class, please read to the + [TmpAuthenticator changelog]'s breaking changes. [jupyterhub changelog]: https://jupyterhub.readthedocs.io/en/stable/changelog.html [kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html +[oauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html +[tmpauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html + +#### Notable dependencies updated + +| Dependency | Version in 2.0.0 | Version in 3.0.0 | Changelog link | Note | +| -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.0 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 15.1.0 (16 soon) | [Changelog](https://oauthenticator.readthedocs.io/en/latest/reference/changelog.html) | Run in the `hub` pod | +| [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.5.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.1 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [tmpauthenticator](https://github.com/jupyterhub/tmpauthenticator) | 0.6 | 1.0.0 | [Changelog](https://github.com/jupyterhub/tmpauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.2.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | +| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.3 | 4.5.5 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | +| [traefik](https://github.com/traefik/traefik) | v2.8.4 | v2.10.1 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.23.10 | v1.26.5 | - | Run in the `user-scheduler` pod(s) | + +For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt) file and use its git history to see what changes between tagged versions. + +#### New features added + +- Add a jupyterhub/k8s-hub-slim image alongside jupyterhub/k8s-hub [#2920](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2920) ([@consideRatio](https://github.com/consideRatio)) + +#### Bugs fixed + +- Fix bugs related to installing chart multiple times in the same namespace [#3032](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3032) ([@HoseonRyu](https://github.com/HoseonRyu)) + +#### Maintenance and upkeep improvements + +- Update kubespawner 5.0.0 to 6.0.0, tmpauthenticator 0.6 to 1.0.0, nativeauthenticator 1.2.0 to 1.2.1, ltiauthenticator 1.5.0 to 1.5.1 [#3129](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3129) ([@jupyterhub-bot](https://github.com/jupyterhub-bot)) +- Update kube-scheduler in user-scheduler from 1.25.9 to 1.26.4 [#3114](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3114) ([@consideRatio](https://github.com/consideRatio)) +- Bump to kubespawner 5.0.0 and tornado 6.3 [#3095](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3095) ([@jupyterhub-bot](https://github.com/jupyterhub-bot)) +- Drop support for k8s 1.22 [#3092](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3092) ([@consideRatio](https://github.com/consideRatio)) +- refactor: rename schema.yaml to values.schema.yaml [#3090](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3090) ([@consideRatio](https://github.com/consideRatio)) +- dependabot: monthly updates of github actions [#3085](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3085) ([@consideRatio](https://github.com/consideRatio)) +- Bump to 3.0.0-0.dev [#3084](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3084) ([@yuvipanda](https://github.com/yuvipanda)) +- Refactor of image-awaiter's dockerfile [#3078](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3078) ([@alekseyolg](https://github.com/alekseyolg)) +- compile psycopg2 in hub image [#3066](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3066) ([@minrk](https://github.com/minrk)) +- satisfy flake8 in jupyterhub_config.py [#3065](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3065) ([@minrk](https://github.com/minrk)) +- Update jupyterhub from 3.1.1 to 4.0.0b1 [#3045](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3045) ([@jupyterhub-bot](https://github.com/jupyterhub-bot)) +- Drop support for k8s 1.21 [#3041](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3041) ([@consideRatio](https://github.com/consideRatio)) +- pre-commit: add flake8 and fix details [#2940](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2940) ([@consideRatio](https://github.com/consideRatio)) +- Drop support for k8s 1.20 [#2936](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2936) ([@consideRatio](https://github.com/consideRatio)) +- Upgrade from python 3.9 to 3.11 in hub and singleuser-sample for performance [#2919](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2919) ([@yuvipanda](https://github.com/yuvipanda)) +- Switch from deprecated k8s.gcr.io to registry.k8s.io [#2910](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2910) ([@consideRatio](https://github.com/consideRatio)) +- values.yaml: fix link to configurable-http-proxy releases [#2881](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2881) ([@manics](https://github.com/manics)) + +#### Documentation improvements + +- Helm chart url has changed [#3122](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3122) ([@manics](https://github.com/manics)) +- Remove double word cluster in installation.md [#3119](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3119) ([@cbowman0](https://github.com/cbowman0)) +- docs: fix readme badge for tests [#3094](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3094) ([@consideRatio](https://github.com/consideRatio)) +- doc: singleuser.uid default is always 1000 [#3079](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3079) ([@manics](https://github.com/manics)) +- Replace IEC prefixes link [#3073](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3073) ([@manics](https://github.com/manics)) +- DOC: Fix invalid names in configuration examples [#3069](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3069) ([@ChristofKaufmann](https://github.com/ChristofKaufmann)) +- Replace microk8s with generic self-hosted doc [#3055](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3055) ([@manics](https://github.com/manics)) +- Revert https://app.gitter.im/#/room/#jupyterhub_jupyterhub:gitter.im … [#3050](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3050) ([@manics](https://github.com/manics)) +- Use jupyterhub docs `stable` instead of `latest` [#3049](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3049) ([@manics](https://github.com/manics)) +- docs: Replace most permanent-redirects from linkcheck [#3048](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3048) ([@manics](https://github.com/manics)) +- docs: user-env default image is not base-image [#3047](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3047) ([@manics](https://github.com/manics)) +- Fix broken link [#3020](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3020) ([@xcompass](https://github.com/xcompass)) +- docs: Update custom image docs to reflect root requirement [#3003](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3003) ([@pnasrat](https://github.com/pnasrat)) +- Documentation fix for running k8s-singleuser-sample locally [#3002](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3002) ([@pnasrat](https://github.com/pnasrat)) +- note at line 554 did not render correctly [#2987](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2987) ([@aaronjnewman](https://github.com/aaronjnewman)) +- docs: AWS master node size needs to be larger than micro [#2956](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2956) ([@arunppsg](https://github.com/arunppsg)) +- docs: update of readthedocs config and docs/source/conf.py [#2909](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2909) ([@consideRatio](https://github.com/consideRatio)) +- docs: fix git sha lookup for dev builds [#2879](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2879) ([@manics](https://github.com/manics)) +- docs: remove /auth from keycloak URLs [#2878](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2878) ([@manics](https://github.com/manics)) +- docs: auth defaults to dummy [#2877](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2877) ([@manics](https://github.com/manics)) +- docs: backfill early changelog entries based on git tags and github releases [#2862](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2862) ([@consideRatio](https://github.com/consideRatio)) + +#### Continuous integration improvements + +- ci: fix deprecation of set-output and use ubuntu 22.04 and py311 [#3068](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3068) ([@consideRatio](https://github.com/consideRatio)) +- Summarise linkcheck CI output [#3051](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3051) ([@manics](https://github.com/manics)) +- ci: fix for redirect to hub.jupyter.org [#3015](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3015) ([@consideRatio](https://github.com/consideRatio)) +- ci: fix vuln-scan regression following set-output deprecation [#2984](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2984) ([@consideRatio](https://github.com/consideRatio)) +- ci: fix deprecation of set-output in github workflows [#2943](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2943) ([@consideRatio](https://github.com/consideRatio)) +- ci: minimize yamllint-config.yaml's complexity [#2939](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2939) ([@consideRatio](https://github.com/consideRatio)) +- ci: minor refactoring/updates of tools [#2938](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2938) ([@consideRatio](https://github.com/consideRatio)) +- ci: bump docker action versions to v2 from v2.x.y [#2914](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2914) ([@consideRatio](https://github.com/consideRatio)) +- docs: Remove unreleased reverted change from 2.0.0 release changelog [#2893](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2893) ([@Uular](https://github.com/Uular)) +- secret sync image: use python 3.9 [#2886](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2886) ([@consideRatio](https://github.com/consideRatio)) +- ci: enable buildkit for vuln scan workflow as needed for --mount [#2885](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2885) ([@consideRatio](https://github.com/consideRatio)) +- ci: Auto-create GitHub release when repo is tagged [#2883](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2883) ([@manics](https://github.com/manics)) + +#### Contributors to this release + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-05-11&type=c)) + +[@aaronjnewman](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-05-31&type=Issues) | [@alekseyolg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-05-31&type=Issues) | [@arunppsg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-05-31&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-05-31&type=Issues) | [@cbowman0](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-05-31&type=Issues) | [@ChristofKaufmann](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-05-31&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-05-31&type=Issues) | [@dasantonym](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-05-31&type=Issues) | [@DeepSkyWonder](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-05-31&type=Issues) | [@ebebpl](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-05-31&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2022-09-09..2023-05-31&type=Issues) | [@HoseonRyu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-05-31&type=Issues) | [@IceS2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-05-31&type=Issues) | [@JunaidChaudry](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-05-31&type=Issues) | [@kanor1306](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-05-31&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-05-31&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-05-31&type=Issues) | [@pnasrat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-05-31&type=Issues) | [@Uular](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-05-31&type=Issues) | [@xcompass](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-05-31&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-05-31&type=Issues) ## 2.0 From 803a8b8e5595573f5641bb39cb3b765f56ede97e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 4 Jun 2023 20:01:40 +0200 Subject: [PATCH 746/898] Update date in changelog --- docs/source/changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 8d4981c480..df8417350f 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,7 +14,7 @@ this list should be updated. ## 3.0 -### 3.0.0-alpha.1 - 2023-05-12 +### 3.0.0-alpha.1 - 2023-06-04 This is an alpha release as additional breaking changes are still planned, see [this issue](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/3091) @@ -91,6 +91,7 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp - Drop support for k8s 1.20 [#2936](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2936) ([@consideRatio](https://github.com/consideRatio)) - Upgrade from python 3.9 to 3.11 in hub and singleuser-sample for performance [#2919](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2919) ([@yuvipanda](https://github.com/yuvipanda)) - Switch from deprecated k8s.gcr.io to registry.k8s.io [#2910](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2910) ([@consideRatio](https://github.com/consideRatio)) +- secret sync image: use python 3.9 [#2886](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2886) ([@consideRatio](https://github.com/consideRatio)) - values.yaml: fix link to configurable-http-proxy releases [#2881](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2881) ([@manics](https://github.com/manics)) #### Documentation improvements @@ -112,6 +113,7 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp - note at line 554 did not render correctly [#2987](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2987) ([@aaronjnewman](https://github.com/aaronjnewman)) - docs: AWS master node size needs to be larger than micro [#2956](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2956) ([@arunppsg](https://github.com/arunppsg)) - docs: update of readthedocs config and docs/source/conf.py [#2909](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2909) ([@consideRatio](https://github.com/consideRatio)) +- docs: Remove unreleased reverted change from 2.0.0 release changelog [#2893](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2893) ([@Uular](https://github.com/Uular)) - docs: fix git sha lookup for dev builds [#2879](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2879) ([@manics](https://github.com/manics)) - docs: remove /auth from keycloak URLs [#2878](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2878) ([@manics](https://github.com/manics)) - docs: auth defaults to dummy [#2877](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2877) ([@manics](https://github.com/manics)) @@ -127,16 +129,14 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp - ci: minimize yamllint-config.yaml's complexity [#2939](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2939) ([@consideRatio](https://github.com/consideRatio)) - ci: minor refactoring/updates of tools [#2938](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2938) ([@consideRatio](https://github.com/consideRatio)) - ci: bump docker action versions to v2 from v2.x.y [#2914](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2914) ([@consideRatio](https://github.com/consideRatio)) -- docs: Remove unreleased reverted change from 2.0.0 release changelog [#2893](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2893) ([@Uular](https://github.com/Uular)) -- secret sync image: use python 3.9 [#2886](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2886) ([@consideRatio](https://github.com/consideRatio)) - ci: enable buildkit for vuln scan workflow as needed for --mount [#2885](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2885) ([@consideRatio](https://github.com/consideRatio)) - ci: Auto-create GitHub release when repo is tagged [#2883](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2883) ([@manics](https://github.com/manics)) #### Contributors to this release -([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-05-11&type=c)) +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-06-04&type=c)) -[@aaronjnewman](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-05-31&type=Issues) | [@alekseyolg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-05-31&type=Issues) | [@arunppsg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-05-31&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-05-31&type=Issues) | [@cbowman0](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-05-31&type=Issues) | [@ChristofKaufmann](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-05-31&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-05-31&type=Issues) | [@dasantonym](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-05-31&type=Issues) | [@DeepSkyWonder](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-05-31&type=Issues) | [@ebebpl](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-05-31&type=Issues) | [@github-actions](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Agithub-actions+updated%3A2022-09-09..2023-05-31&type=Issues) | [@HoseonRyu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-05-31&type=Issues) | [@IceS2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-05-31&type=Issues) | [@JunaidChaudry](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-05-31&type=Issues) | [@kanor1306](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-05-31&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-05-31&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-05-31&type=Issues) | [@pnasrat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-05-31&type=Issues) | [@Uular](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-05-31&type=Issues) | [@xcompass](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-05-31&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-05-31&type=Issues) +@aaronjnewman ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-06-04&type=Issues)) | @alekseyolg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-06-04&type=Issues)) | @arunppsg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-06-04&type=Issues)) | @betatim ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-06-04&type=Issues)) | @bjornjorgensen ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornjorgensen+updated%3A2022-09-09..2023-06-04&type=Issues)) | @cbowman0 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-06-04&type=Issues)) | @choldgraf ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2022-09-09..2023-06-04&type=Issues)) | @ChristofKaufmann ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-06-04&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-06-04&type=Issues)) | @dasantonym ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-06-04&type=Issues)) | @DeepSkyWonder ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-06-04&type=Issues)) | @ebebpl ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-06-04&type=Issues)) | @HoseonRyu ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-06-04&type=Issues)) | @IceS2 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-06-04&type=Issues)) | @JunaidChaudry ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-06-04&type=Issues)) | @jupyterhub-bot ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2022-09-09..2023-06-04&type=Issues)) | @kanor1306 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-06-04&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-06-04&type=Issues)) | @mathbunnyru ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amathbunnyru+updated%3A2022-09-09..2023-06-04&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-06-04&type=Issues)) | @pnasrat ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-06-04&type=Issues)) | @Uular ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-06-04&type=Issues)) | @xcompass ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-06-04&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-06-04&type=Issues)) ## 2.0 From 081650d94bbd64f0e983cbda0513b1350e30c195 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 4 Jun 2023 20:10:25 +0200 Subject: [PATCH 747/898] Update version in tbump.toml --- chartpress.yaml | 2 ++ tbump.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/chartpress.yaml b/chartpress.yaml index 8b94b1bada..cf7f09f768 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -18,6 +18,8 @@ charts: # merged so far into the main branch. If for example we have merged a # breaking change it should be the next major version, like 3.0.0-0.dev. # + # baseVersion should be managed via tbump, see RELEASE.md for details + # baseVersion: "3.0.0-0.dev" repo: git: jupyterhub/helm-chart diff --git a/tbump.toml b/tbump.toml index 574314a4ba..93855946c3 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "2.0.1-0.dev" +current = "3.0.0-0.dev" # match our prerelease prefixes # -alpha.1 From 5e0e02805a459da067bf030890a3b8922323ca7f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 4 Jun 2023 20:10:45 +0200 Subject: [PATCH 748/898] Bump to 3.0.0-alpha.1 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index cf7f09f768..3009cd6044 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0-0.dev" + baseVersion: "3.0.0-alpha.1" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 93855946c3..2ce0ba511d 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0-0.dev" +current = "3.0.0-alpha.1" # match our prerelease prefixes # -alpha.1 From 309e08c9bd93af93680ef362db894e190a2a6741 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 04:14:17 +0000 Subject: [PATCH 749/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.3.2 → v3.4.0](https://github.com/asottile/pyupgrade/compare/v3.3.2...v3.4.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ba593a05c..a464db73ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.3.2 + rev: v3.4.0 hooks: - id: pyupgrade args: From 134633d63afbea2c275715e0b0291134ce3cee21 Mon Sep 17 00:00:00 2001 From: Shota Matsumoto Date: Thu, 8 Jun 2023 17:29:23 +0900 Subject: [PATCH 750/898] Add fsGroupChangePolicy: OnRootMismatch --- jupyterhub/templates/schedulable-notebook/deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/templates/schedulable-notebook/deployment.yaml b/jupyterhub/templates/schedulable-notebook/deployment.yaml index dcf6ff6dd1..1ac1aec661 100644 --- a/jupyterhub/templates/schedulable-notebook/deployment.yaml +++ b/jupyterhub/templates/schedulable-notebook/deployment.yaml @@ -32,6 +32,7 @@ spec: claimName: schedulable-notebook-home securityContext: fsGroup: {{ .Values.hub.fsGid }} + fsGroupChangePolicy: "OnRootMismatch" containers: - command: - jupyterhub-singleuser From 3c79ba3b39d46a0a9280d1548c0c3feb58e64114 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 9 Jun 2023 05:09:32 +0000 Subject: [PATCH 751/898] Update jupyterhub from 4.0.0 to 4.0.1 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 6 +++--- jupyterhub/Chart.yaml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index de0bab8cd8..f3b0e4a811 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0 +jupyterhub==4.0.1 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 46f7884926..0201734ac3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -62,7 +62,7 @@ jsonschema==4.17.3 # oauthenticator jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.0 +jupyterhub==4.0.1 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -202,7 +202,7 @@ typing-extensions==4.6.3 # via # alembic # sqlalchemy -urllib3==2.0.2 +urllib3==2.0.3 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 1fc17a3652..496675c93f 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.0 +jupyterhub==4.0.1 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 03288c91d0..cb67f29a23 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -134,9 +134,9 @@ jupyter-server-terminals==0.4.4 # via jupyter-server jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.0 +jupyterhub==4.0.1 # via -r requirements.in -jupyterlab==4.0.1 +jupyterlab==4.0.2 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert @@ -335,7 +335,7 @@ typing-extensions==4.6.3 # sqlalchemy uri-template==1.2.0 # via jsonschema -urllib3==2.0.2 +urllib3==2.0.3 # via requests wcwidth==0.2.6 # via prompt-toolkit diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 73ec860307..9005a6ecb2 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "4.0.0" +appVersion: "4.0.1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From 73c2d4d9e6265739db2d47189f2ff89de8cf9d56 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 12 Jun 2023 10:11:50 +0200 Subject: [PATCH 752/898] Update changelog for 3.0.0-beta.1 Let's not require oauthenticator 16 to be released as it is a big release that isn't done yet that could justify a dedicated major release of z2jh. --- docs/source/changelog.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index df8417350f..6f394e2d0b 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,11 +14,10 @@ this list should be updated. ## 3.0 -### 3.0.0-alpha.1 - 2023-06-04 +### 3.0.0-beta.1 - 2023-06-12 -This is an alpha release as additional breaking changes are still planned, see -[this issue](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/3091) -for details. +This is a beta release for testing before the 3.0.0 release, no further changes +are planned. #### Breaking changes @@ -35,25 +34,21 @@ for details. configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. -- OAuthenticator 15.1.0 _will be_ upgraded to 16.0.0, _but isn't yet in the alpha.1 release_ - - If you are using an JupyterHub Authenticator class from this package, please - read to the [OAuthenticator changelog]'s breaking changes. - TmpAuthenticator 0.6 is upgraded to 1.0.0 - If you are using this JupyterHub Authenticator class, please read to the [TmpAuthenticator changelog]'s breaking changes. [jupyterhub changelog]: https://jupyterhub.readthedocs.io/en/stable/changelog.html [kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html -[oauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html [tmpauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html #### Notable dependencies updated | Dependency | Version in 2.0.0 | Version in 3.0.0 | Changelog link | Note | | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | -| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.0 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 15.1.0 (16 soon) | [Changelog](https://oauthenticator.readthedocs.io/en/latest/reference/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 15.1.0 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.5.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.1 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | @@ -98,6 +93,7 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp - Helm chart url has changed [#3122](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3122) ([@manics](https://github.com/manics)) - Remove double word cluster in installation.md [#3119](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3119) ([@cbowman0](https://github.com/cbowman0)) +- Clarify `hub.config` can configure KubeSpawner and more [#3104](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3104) ([@JunaidChaudry](https://github.com/JunaidChaudry)) - docs: fix readme badge for tests [#3094](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3094) ([@consideRatio](https://github.com/consideRatio)) - doc: singleuser.uid default is always 1000 [#3079](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3079) ([@manics](https://github.com/manics)) - Replace IEC prefixes link [#3073](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3073) ([@manics](https://github.com/manics)) From 28cee60a4a86a419ed11e78186ecb5251d218f6e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 12 Jun 2023 10:37:35 +0200 Subject: [PATCH 753/898] docs: fix various broken links and redirects --- .github/workflows/test-chart.yaml | 8 ++++---- CONTRIBUTING.md | 2 +- README.md | 6 +++--- docs/source/administrator/authentication.md | 12 ++++++------ docs/source/changelog.md | 8 ++++---- .../source/jupyterhub/customizing/user-management.md | 2 +- docs/source/jupyterhub/installation.md | 4 ++-- docs/source/kubernetes/amazon/step-zero-aws-eks.md | 5 ++--- docs/source/kubernetes/microsoft/step-zero-azure.md | 4 ++-- docs/source/repo2docker.md | 4 +++- jupyterhub/Chart.yaml | 2 +- jupyterhub/README.md | 6 +++--- jupyterhub/values.schema.yaml | 4 ++-- 13 files changed, 34 insertions(+), 33 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 539d55414d..e8aca1bcd1 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -158,7 +158,7 @@ jobs: # # The upgrade-from input should be a chart version, or match the version # information from - # https://jupyterhub.github.io/helm-chart/info.json + # https://hub.jupyter.org/helm-chart/info.json # - k3s-channel: v1.25 test: upgrade @@ -239,7 +239,7 @@ jobs: # reach the ACME client in our autohttps pod. - name: Install local ACME server run: | - helm install pebble --repo https://jupyterhub.github.io/helm-chart/ pebble --values dev-config-pebble.yaml + helm install pebble --repo https://hub.jupyter.org/helm-chart/ pebble --values dev-config-pebble.yaml # Build our images if needed and update values.yaml with the tags - name: Install and run chartpress @@ -280,7 +280,7 @@ jobs: run: | . ./ci/common if [ ${{ matrix.upgrade-from }} = stable -o ${{ matrix.upgrade-from }} = dev ]; then - UPGRADE_FROM_VERSION=$(curl -sSL https://jupyterhub.github.io/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') + UPGRADE_FROM_VERSION=$(curl -sSL https://hub.jupyter.org/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') else UPGRADE_FROM_VERSION=${{ matrix.upgrade-from }} fi @@ -294,7 +294,7 @@ jobs: # # https://github.com/helm/helm/issues/9244 cd ci - helm install jupyterhub --repo https://jupyterhub.github.io/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} + helm install jupyterhub --repo https://hub.jupyter.org/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} - name: "(Upgrade) Install helm diff" if: matrix.test == 'upgrade' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 538c997c80..e607d4302d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -174,7 +174,7 @@ For more information, see **Install Pebble** ```shell -helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/ +helm repo add jupyterhub https://hub.jupyter.org/helm-chart/ helm repo update helm upgrade --install pebble jupyterhub/pebble --cleanup-on-fail --values dev-config-pebble.yaml ``` diff --git a/README.md b/README.md index fe05547b7e..4a646cd285 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ [![Documentation build status](https://img.shields.io/readthedocs/zero-to-jupyterhub?logo=read-the-docs)](https://zero-to-jupyterhub.readthedocs.io/en/latest/?badge=latest) [![GitHub Workflow Status - Test](https://img.shields.io/github/actions/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/test-chart.yaml?logo=github&label=tests)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) [![GitHub Workflow Status - Vuln. scan](https://img.shields.io/github/actions/workflow/status/jupyterhub/zero-to-jupyterhub-k8s/vuln-scan.yaml?logo=github&label=Vuln.%20scan)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions) -[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=stable&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.stable&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#jupyterhub) -[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=pre&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.pre&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) -[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=dev&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.latest&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) +[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=stable&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.stable&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#jupyterhub) +[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=pre&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.pre&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) +[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=dev&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.latest&colorB=orange&logo=helm)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub)
[![GitHub](https://img.shields.io/badge/issue_tracking-github-blue?logo=github)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues) [![Discourse](https://img.shields.io/badge/help_forum-discourse-blue?logo=discourse)](https://discourse.jupyter.org/c/jupyterhub/z2jh-k8s) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 7640c2b6fd..ad4a9475ea 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -13,7 +13,7 @@ Before configuring this, you should have [setup HTTPS](https). ### Authenticator classes -Z2JH defaults to a [DummyAuthenticator](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.DummyAuthenticator) +Z2JH defaults to a [DummyAuthenticator](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.DummyAuthenticator) that allows anyone to login with any username and password. This should only be used for testing purposes. @@ -39,10 +39,10 @@ authenticator class itself through this Helm chart's As all authenticator classes derive from the `Authenticator` base class, they share some configuration options. Below are some common configuration options, but please refer to the official [configuration -reference](https://jupyterhub.readthedocs.io/en/stable/api/auth.html) for more +reference](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html) for more details. -### [allowed_users](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.allowed_users) / [admin_users](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.LocalAuthenticator.admin_users) +### [allowed_users](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.Authenticator.allowed_users) / [admin_users](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.LocalAuthenticator.admin_users) Some authenticator classes may have dedicated logic in addition this this to authorize users. @@ -70,7 +70,7 @@ In the above configuration, we have configured three things: 2. anyone will be able to login with username `user1-4` and the password `a-shared-secret-password` 3. `user1` and `user2` will have admin permissions, while `user3` and `user4` will be regular users. -### [auto_login](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.auto_login) +### [auto_login](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.Authenticator.auto_login) If you have configured authentication with GitHub for example, the page `/hub/login` will feature a single orange button that users are to press to @@ -84,7 +84,7 @@ hub: auto_login: true ``` -### [enable_auth_state](https://jupyterhub.readthedocs.io/en/stable/api/auth.html#jupyterhub.auth.Authenticator.enable_auth_state) +### [enable_auth_state](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.Authenticator.enable_auth_state) If you want JupyterHub to persist often sensitive information received as part of logging in, you need to enable it. @@ -343,7 +343,7 @@ hub: [OpenID Connect](https://openid.net/connect) is an identity layer on top of the OAuth 2.0 protocol, implemented by [various servers and -services](https://openid.net/developers/certified/#OPServices). While OpenID +services](https://openid.net/certified-open-id-developer-tools/). While OpenID Connect endpoint discovery is not supported by oauthentiator, you can still configure JupyterHub to authenticate with OpenID Connect providers by specifying all endpoints in the GenericOAuthenticator class. diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6f394e2d0b..b39bb8c1e7 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -7,7 +7,7 @@ Here you can find upgrade changes in between releases and upgrade instructions. ## Unreleased breaking changes This Helm chart provides [development -releases](https://jupyterhub.github.io/helm-chart/#development-releases-jupyterhub), +releases](https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub), and as we merge [breaking changes in pull requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), this list should be updated. @@ -2392,7 +2392,7 @@ Helm cannot upgrade from the labelling scheme in 0.6 to that in 0.7 without `--f RELEASE_NAME= NAMESPACE= -helm repo add jupyterhub https://jupyterhub.github.io/helm-chart/ +helm repo add jupyterhub https://hub.jupyter.org/helm-chart/ helm repo update # NOTE: We need the --force flag to allow recreation of resources that can't be @@ -3182,7 +3182,7 @@ but this is a good start! ##### Better Azure support -Azure's new managed Kubernetes service ([AKS](https://docs.microsoft.com/en-us/azure/aks/)) is much +Azure's new managed Kubernetes service ([AKS](https://learn.microsoft.com/en-us/azure/aks/)) is much better supported by this version! - We have much better documentation on using z2jh with Azure! @@ -3279,7 +3279,7 @@ In alphabetical order, ## 0.5 -### 0.5 - [Hamid Hassan](https://www.espncricinfo.com/player/hamid-hassan-311427) - 2017-12-05 +### 0.5 - [Hamid Hassan](https://www.espncricinfo.com/cricketers/hamid-hassan-311427) - 2017-12-05 JupyterHub 0.8, HTTPS & scalability. diff --git a/docs/source/jupyterhub/customizing/user-management.md b/docs/source/jupyterhub/customizing/user-management.md index a79450f238..611ed3ce5e 100644 --- a/docs/source/jupyterhub/customizing/user-management.md +++ b/docs/source/jupyterhub/customizing/user-management.md @@ -84,7 +84,7 @@ are being used as expected. ## Admin Users JupyterHub has the concept of -[admin users](https://jupyterhub.readthedocs.io/en/stable/getting-started/authenticators-users-basics.html#configure-admins-admin-users) +[admin users](https://jupyterhub.readthedocs.io/en/stable/tutorial/getting-started/authenticators-users-basics.html#configure-admins-admin-users) who have special rights. They can start / stop other user's servers, and optionally access user's notebooks. They will see a new **Admin** button in their Control Panel which will take them to an **Admin Panel** where they can diff --git a/docs/source/jupyterhub/installation.md b/docs/source/jupyterhub/installation.md index a771935033..4f3fe1f0d2 100644 --- a/docs/source/jupyterhub/installation.md +++ b/docs/source/jupyterhub/installation.md @@ -35,7 +35,7 @@ just create a `config.yaml` file with some helpful comments. # Introduction to YAML: https://www.youtube.com/watch?v=cdLNKUoMc6c # Chart config reference: https://zero-to-jupyterhub.readthedocs.io/en/stable/resources/reference.html # Chart default values: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/jupyterhub/values.yaml -# Available chart versions: https://jupyterhub.github.io/helm-chart/ +# Available chart versions: https://hub.jupyter.org/helm-chart/ # ``` @@ -101,7 +101,7 @@ can try with `nano config.yaml`. Helm chart is paired with a specific version of JupyterHub. E.g., `0.11.1` of the Helm chart runs JupyterHub `1.3.0`. For a list of which JupyterHub version is installed in each version - of the JupyterHub Helm Chart, see the [Helm Chart repository](https://jupyterhub.github.io/helm-chart/). + of the JupyterHub Helm Chart, see the [Helm Chart repository](https://hub.jupyter.org/helm-chart/). 3. While Step 2 is running, you can see the pods being created by entering in a different terminal: diff --git a/docs/source/kubernetes/amazon/step-zero-aws-eks.md b/docs/source/kubernetes/amazon/step-zero-aws-eks.md index 91ed1dacb5..bcc5769221 100644 --- a/docs/source/kubernetes/amazon/step-zero-aws-eks.md +++ b/docs/source/kubernetes/amazon/step-zero-aws-eks.md @@ -87,6 +87,5 @@ This guide uses AWS to set up a cluster. This mirrors the steps found at [Gettin ## Cluster Autoscaler -If you'd like to do some {ref}`optimizations `, you need to deploy Cluster Autoscaler (CA) first. - -See +If you'd like to do some {ref}`optimizations `, +you need to deploy Cluster Autoscaler (CA) first. diff --git a/docs/source/kubernetes/microsoft/step-zero-azure.md b/docs/source/kubernetes/microsoft/step-zero-azure.md index c08ca72d50..e578f36afe 100644 --- a/docs/source/kubernetes/microsoft/step-zero-azure.md +++ b/docs/source/kubernetes/microsoft/step-zero-azure.md @@ -30,7 +30,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta - **Install command-line tools locally**. You can access the Azure CLI via a package that you can install locally. - To do so, first follow the [installation instructions](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) in the + To do so, first follow the [installation instructions](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) in the Azure documentation. Then run the following command to connect your local CLI with your account: @@ -85,7 +85,7 @@ If you prefer to use the Azure portal see the [Azure Kubernetes Service quicksta format, rather than the default JSON output. We shall use this with most commands when executing them by hand. - Consider [setting a cloud budget](https://docs.microsoft.com/en-us/partner-center/set-an-azure-spending-budget-for-your-customers) + Consider [setting a cloud budget](https://learn.microsoft.com/en-us/partner-center/set-an-azure-spending-budget-for-your-customers) for your Azure account in order to make sure you don't accidentally spend more than you wish to. diff --git a/docs/source/repo2docker.md b/docs/source/repo2docker.md index 94e8f0bf41..ef370c9a9a 100644 --- a/docs/source/repo2docker.md +++ b/docs/source/repo2docker.md @@ -72,7 +72,9 @@ to configure JupyterHub to build off of this image: 4. **Get credentials for a docker repository.** The image you will build for your JupyterHub must be made available by being - published to some container registry. You could for example use [Docker Hub](https://hub.docker.com/) or [Google Container Registry](https://cloud.google.com/container-registry/). + published to some container registry. You could for example use [Docker Hub](https://hub.docker.com/) or [Google Container Registry](https://cloud.google.com/artifact-registry). + + In the next step, you need an image reference for you and others to find your image with. diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 9005a6ecb2..7a7beefdc1 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -7,7 +7,7 @@ description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org sources: [https://github.com/jupyterhub/zero-to-jupyterhub-k8s] -icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg +icon: https://hub.jupyter.org/helm-chart/images/hublogo.svg kubeVersion: ">=1.23.0-0" maintainers: # Since it is a requirement of Artifact Hub to have specific maintainers diff --git a/jupyterhub/README.md b/jupyterhub/README.md index bb85ebb2bd..954d4653a0 100644 --- a/jupyterhub/README.md +++ b/jupyterhub/README.md @@ -5,9 +5,9 @@ [![Discourse](https://img.shields.io/badge/Help_forum-discourse-blue?logo=discourse&logoColor=white)](https://discourse.jupyter.org/c/jupyterhub/z2jh-k8s) [![Gitter](https://img.shields.io/badge/Social_chat-gitter-blue?logo=gitter&logoColor=white)](https://gitter.im/jupyterhub/jupyterhub)
-[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20stable%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.stable&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#jupyterhub) -[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20pre-release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.pre&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) -[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20dev%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.latest&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) +[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20stable%20release&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.stable&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#jupyterhub) +[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20pre-release&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.pre&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) +[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20dev%20release&url=https://hub.jupyter.org/helm-chart/info.json&query=$.jupyterhub.latest&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) The JupyterHub Helm chart is accompanied with an installation guide at [z2jh.jupyter.org](https://z2jh.jupyter.org). Together they enable you to deploy [JupyterHub](https://jupyterhub.readthedocs.io) in a Kubernetes cluster that can make Jupyter environments available to several thousands of simultaneous users. diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 51324ebeb9..14aa32b046 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -1135,7 +1135,7 @@ properties: type: [integer, "null"] description: &jupyterhub-native-config-description | JupyterHub native configuration, see the [JupyterHub - documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html) + documentation](https://jupyterhub.readthedocs.io/en/stable/reference/api/app.html) for more information. allowNamedServers: type: [boolean, "null"] @@ -1241,7 +1241,7 @@ properties: This is where you register JupyterHub services. For details on how to configure these services in this Helm chart just keep reading but for details on services themselves instead read [JupyterHub's - documentation](https://jupyterhub.readthedocs.io/en/stable/api/service.html). + documentation](https://jupyterhub.readthedocs.io/en/stable/reference/api/service.html). ```{note} Only a selection of JupyterHub's configuration options that can be From d9b4ca96c0a9ccb4f016b44c45755a16574c5e65 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 12 Jun 2023 14:54:46 +0200 Subject: [PATCH 754/898] Bump to 3.0.0-beta.1 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 3009cd6044..77a818e1ad 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0-alpha.1" + baseVersion: "3.0.0-beta.1" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 2ce0ba511d..76391c821c 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0-alpha.1" +current = "3.0.0-beta.1" # match our prerelease prefixes # -alpha.1 From 8be466fdef1a180b7115493887b596e018a9ec1b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Wed, 14 Jun 2023 17:27:35 +0100 Subject: [PATCH 755/898] Show default value in configuration reference Parses values.yaml to get the default value --- docs/source/conf.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 9a9ed157e0..a5b85e062e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -93,6 +93,21 @@ def _get_git_ref_from_chartpress_based_version(version): data = yaml.safe_load(f) +# default_values +with open("../../jupyterhub/values.yaml") as f: + default_values = yaml.safe_load(f) + + +def get_default_value(k): + """ + Get the default value from values.yaml + """ + v = default_values + for key in k.split("."): + v = v[key] + return v + + def parse_schema(d, md=[], depth=0, pre=""): """ Generate markdown headers from a passed python dictionary created by @@ -111,7 +126,17 @@ def parse_schema(d, md=[], depth=0, pre=""): if "description" in val: for ln in val["description"].split("\n"): md.append(ln) - md.append("") + try: + def_value = get_default_value(f"{pre}{key}") + if def_value is not None and def_value not in ( + "set-by-chartpress", + "", + ): + md.append(f"default: `{def_value}`") + md.append("") + except KeyError: + # TODO: Should we error if the property isn't in values.yaml? + pass parse_schema(val, md, depth, f"{pre}{key}.") depth -= 1 From 104475a3d942457982b0bb1e144d78f2d6c808ee Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 15 Jun 2023 05:09:06 +0000 Subject: [PATCH 756/898] Update kube-scheduler version from v1.26.5 to v1.26.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 96dd9bb714..0afdd9722f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -506,7 +506,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.5" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.6" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From dc1be80ad1a1172ae666d43c1b1ea78a103ec7fc Mon Sep 17 00:00:00 2001 From: Simon Li Date: Thu, 15 Jun 2023 13:03:25 +0100 Subject: [PATCH 757/898] Show value default for non-objects, use json representation --- docs/source/conf.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index a5b85e062e..8c8f86dccb 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,6 +3,7 @@ # Configuration reference: https://www.sphinx-doc.org/en/master/usage/configuration.html # import datetime +import json import os import re import subprocess @@ -126,17 +127,23 @@ def parse_schema(d, md=[], depth=0, pre=""): if "description" in val: for ln in val["description"].split("\n"): md.append(ln) - try: - def_value = get_default_value(f"{pre}{key}") - if def_value is not None and def_value not in ( + try: + def_value = get_default_value(f"{pre}{key}") + if ( + def_value is not None + and not isinstance(def_value, dict) + and def_value + not in ( "set-by-chartpress", "", - ): - md.append(f"default: `{def_value}`") - md.append("") - except KeyError: - # TODO: Should we error if the property isn't in values.yaml? - pass + ) + ): + # Use the JSON string representation instead of Python + md.append(f"_Default:_ `{json.dumps(def_value)}`") + md.append("") + except KeyError: + # TODO: Should we error if the property isn't in values.yaml? + pass parse_schema(val, md, depth, f"{pre}{key}.") depth -= 1 From 7353bee74e3ef11b843ce5eec49865a675d4c209 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 19 Jun 2023 05:12:50 +0000 Subject: [PATCH 758/898] Patch known vulnerability in network-tools --- images/network-tools/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index 7002d03ae5..bfe4aae199 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ FROM alpine:3 -# VULN_SCAN_TIME=2023-05-22_05:12:45 +# VULN_SCAN_TIME=2023-06-19_05:12:47 RUN apk add --no-cache iptables From 7b44299ae827e34a295d2ab814dd2fe67312e861 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 21 Jun 2023 05:09:57 +0000 Subject: [PATCH 759/898] Update library/traefik version from v2.10.1 to v2.10.3 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 0afdd9722f..3fc7bbc43d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -249,7 +249,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.10.1" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.10.3" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 0e46771a1dbb3509a1a6eec5bd14fdc733f63c86 Mon Sep 17 00:00:00 2001 From: vhash <29121316+LucasVanHaaren@users.noreply.github.com> Date: Wed, 28 Jun 2023 15:45:31 +0200 Subject: [PATCH 760/898] enhance keycloak configuration example --- docs/source/administrator/authentication.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index ad4a9475ea..508a012d24 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -397,6 +397,12 @@ hub: username_key: preferred_username userdata_params: state: state + # In order to use keycloak client's roles as autorisation layer + claim_groups_key: roles + allowed_groups: + - user + admin_groups: + - admin JupyterHub: authenticator_class: generic-oauth ``` From 61768a25189277fd4e71bd24f417f9cad95f0277 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 28 Jun 2023 17:37:55 +0200 Subject: [PATCH 761/898] Fix spelling detail --- docs/source/administrator/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 508a012d24..810c3728c3 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -397,7 +397,7 @@ hub: username_key: preferred_username userdata_params: state: state - # In order to use keycloak client's roles as autorisation layer + # In order to use keycloak client's roles as authorization layer claim_groups_key: roles allowed_groups: - user From 1020c947d8c65e8607c2f005ac592129c578d4f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 05:50:44 +0000 Subject: [PATCH 762/898] build(deps): bump aquasecurity/trivy-action from 0.10.0 to 0.11.2 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.10.0 to 0.11.2. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/e5f43133f6e8736992c9f3c1b3296e24b37e17f2...41f05d9ecffa2ed3f1580af306000f734b733e54) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index caacc6fa94..4e0232eba2 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 + uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 + uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@e5f43133f6e8736992c9f3c1b3296e24b37e17f2 + uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 with: image-ref: rebuilt-image format: table From ef21a40d8a3f8d7002f036d3a332d986e88aa6a9 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 3 Jul 2023 05:15:41 +0000 Subject: [PATCH 763/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index e4d9a4ccae..d8eeac2248 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-06-04_17:52:10 +# VULN_SCAN_TIME=2023-07-03_05:15:39 # The build stage From cb5a8b1da647065826883b8dd0d2369d51d6eeab Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 3 Jul 2023 05:15:56 +0000 Subject: [PATCH 764/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index de0a65492f..4a28e98f21 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-06-04_17:52:11 +# VULN_SCAN_TIME=2023-07-03_05:15:54 # The build stage From 7072d6de3d9197b943e65551bf66bc15e1fb9308 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 06:28:58 +0000 Subject: [PATCH 765/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.4.0 → v3.8.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a464db73ff..3eb666daa6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.4.0 + rev: v3.8.0 hooks: - id: pyupgrade args: From b4f3943daf8635e44ff07a9ff78eb8080418988f Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:54:08 +0000 Subject: [PATCH 766/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 19 +++++++------- images/singleuser-sample/requirements.txt | 30 +++++++++++------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 0201734ac3..118460d0f2 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -82,7 +82,7 @@ jupyterhub-kubespawner==6.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in -jupyterhub-ltiauthenticator==1.5.1 +jupyterhub-ltiauthenticator==1.6.1 # via -r requirements.in jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in @@ -106,10 +106,8 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==15.1.0 - # via - # -r requirements.in - # jupyterhub-ltiauthenticator +oauthenticator==16.0.0 + # via -r requirements.in oauthlib==3.2.2 # via # jupyterhub @@ -137,7 +135,7 @@ pyjwt[crypto]==2.7.0 # -r requirements.in # jupyterhub-ltiauthenticator # mwoauth -pymysql==1.0.3 +pymysql==1.1.0 # via -r requirements.in pyopenssl==23.2.0 # via certipy @@ -164,7 +162,7 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -ruamel-yaml==0.17.31 +ruamel-yaml==0.17.32 # via # jupyter-telemetry # oauthenticator @@ -176,7 +174,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.15 +sqlalchemy==2.0.17 # via # alembic # jupyterhub @@ -192,13 +190,16 @@ tornado==6.3.2 # jupyterhub # jupyterhub-idle-culler # jupyterhub-ldapauthenticator + # oauthenticator traitlets==5.9.0 # via # jupyter-telemetry # jupyterhub # jupyterhub-kubespawner # jupyterhub-ldapauthenticator -typing-extensions==4.6.3 + # jupyterhub-ltiauthenticator + # oauthenticator +typing-extensions==4.7.1 # via # alembic # sqlalchemy diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index cb67f29a23..03ab0c2567 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -66,7 +66,7 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.23.1 +ipykernel==6.24.0 # via # jupyterlab # nbclassic @@ -92,7 +92,7 @@ jinja2==3.1.2 # notebook json5==0.9.14 # via jupyterlab-server -jsonpointer==2.3 +jsonpointer==2.4 # via jsonschema jsonschema[format-nongpl]==4.17.3 # via @@ -100,14 +100,14 @@ jsonschema[format-nongpl]==4.17.3 # jupyter-telemetry # jupyterlab-server # nbformat -jupyter-client==8.2.0 +jupyter-client==8.3.0 # via # ipykernel # jupyter-server # nbclassic # nbclient # notebook -jupyter-core==5.3.0 +jupyter-core==5.3.1 # via # ipykernel # jupyter-client @@ -122,7 +122,7 @@ jupyter-events==0.6.3 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab -jupyter-server==2.6.0 +jupyter-server==2.7.0 # via # jupyter-lsp # jupyterlab @@ -140,7 +140,7 @@ jupyterlab==4.0.2 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.22.1 +jupyterlab-server==2.23.0 # via jupyterlab mako==1.2.4 # via alembic @@ -153,7 +153,7 @@ matplotlib-inline==0.1.6 # via # ipykernel # ipython -mistune==2.0.5 +mistune==3.0.1 # via nbconvert nbclassic==1.0.0 # via @@ -161,7 +161,7 @@ nbclassic==1.0.0 # notebook nbclient==0.8.0 # via nbconvert -nbconvert==7.4.0 +nbconvert==7.6.0 # via # jupyter-server # nbclassic @@ -208,7 +208,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.5.1 +platformdirs==3.8.0 # via jupyter-core prometheus-client==0.17.0 # via @@ -216,7 +216,7 @@ prometheus-client==0.17.0 # jupyterhub # nbclassic # notebook -prompt-toolkit==3.0.38 +prompt-toolkit==3.0.39 # via ipython psutil==5.9.5 # via ipykernel @@ -266,7 +266,7 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -ruamel-yaml==0.17.31 +ruamel-yaml==0.17.32 # via jupyter-telemetry ruamel-yaml-clib==0.2.7 # via ruamel-yaml @@ -285,7 +285,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.15 +sqlalchemy==2.0.17 # via # alembic # jupyterhub @@ -328,12 +328,12 @@ traitlets==5.9.0 # nbconvert # nbformat # notebook -typing-extensions==4.6.3 +typing-extensions==4.7.1 # via # alembic # async-lru # sqlalchemy -uri-template==1.2.0 +uri-template==1.3.0 # via jsonschema urllib3==2.0.3 # via requests @@ -345,5 +345,5 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.5.2 +websocket-client==1.6.1 # via jupyter-server From 7dc453c189015a9f243a3d363ece109a18915969 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 14:06:25 +0200 Subject: [PATCH 767/898] Add changelog for 3.0.0-beta.2 --- docs/source/changelog.md | 58 ++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index b39bb8c1e7..f9d4dbe06a 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -6,24 +6,30 @@ Here you can find upgrade changes in between releases and upgrade instructions. ## Unreleased breaking changes -This Helm chart provides [development -releases](https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub), -and as we merge [breaking changes in pull -requests](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking), -this list should be updated. +This Helm chart provides [development releases], and as we merge [breaking +changes in pull requests], this list should be updated. + +[development releases]: https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub +[breaking changes in pull requests]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking ## 3.0 -### 3.0.0-beta.1 - 2023-06-12 +### 3.0.0-beta.2 - 2023-07-05 + +This is a beta release for testing before the 3.0.0 release. -This is a beta release for testing before the 3.0.0 release, no further changes -are planned. +```{warning} +Since 3.0.0-beta.1 release 2023-06-12, another breaking change was made by +upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the +[OAuthenticator changelog]'s breaking changes before upgrading from 3.0.0-beta.1 +to 3.0.0-beta.2. +``` #### Breaking changes - K8s 1.23 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. -- JupyterHub 3.0.0 is upgraded to 4.0.0. +- JupyterHub 3.0.0 is upgraded to 4.0.1. - Please refer to the [JupyterHub changelog] for details, but note that this upgrade won't require user servers to be restarted or first install version 4 of `jupyterhub` (PyPI) or `jupyterhub-base` (conda-forge) in their user @@ -34,31 +40,41 @@ are planned. configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. +- OAuthenticator 15.1.0 is upgraded to 16.0.0. + - If you are using a JupyterHub Authenticator class from this project, please + read to the [OAuthenticator changelog]'s breaking changes before upgrading + this Helm chart. - TmpAuthenticator 0.6 is upgraded to 1.0.0 - If you are using this JupyterHub Authenticator class, please read to the - [TmpAuthenticator changelog]'s breaking changes. + [TmpAuthenticator changelog]'s breaking changes before upgrading this Helm + chart. [jupyterhub changelog]: https://jupyterhub.readthedocs.io/en/stable/changelog.html -[kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html -[tmpauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html +[kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html +[oauthenticator changelog]: https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html +[tmpauthenticator changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html #### Notable dependencies updated | Dependency | Version in 2.0.0 | Version in 3.0.0 | Changelog link | Note | | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | | [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | -| [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/latest/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 15.1.0 | [Changelog](https://oauthenticator.readthedocs.io/en/latest/reference/changelog.html) | Run in the `hub` pod | +| [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.0 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | -| [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.5.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.6.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.1 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [tmpauthenticator](https://github.com/jupyterhub/tmpauthenticator) | 0.6 | 1.0.0 | [Changelog](https://github.com/jupyterhub/tmpauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.2.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | | [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.3 | 4.5.5 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | -| [traefik](https://github.com/traefik/traefik) | v2.8.4 | v2.10.1 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | -| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.23.10 | v1.26.5 | - | Run in the `user-scheduler` pod(s) | +| [traefik](https://github.com/traefik/traefik) | v2.8.4 | v2.10.3 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.23.10 | v1.26.6 | [Changelog](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG) | Run in the `user-scheduler` pod(s) | -For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt) file and use its git history to see what changes between tagged versions. +For a detailed list of Python dependencies in the `hub` Pod's Docker image, +inspect the [images/hub/requirements.txt] file and use its git history to see +what changes between tagged versions. + +[images/hub/requirements.txt]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt #### New features added @@ -91,6 +107,8 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Documentation improvements +- Enhance keycloak configuration example [#3142](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3142) ([@LucasVanHaaren](https://github.com/LucasVanHaaren)) +- Show default value in configuration reference [#3138](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3138) ([@manics](https://github.com/manics)) - Helm chart url has changed [#3122](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3122) ([@manics](https://github.com/manics)) - Remove double word cluster in installation.md [#3119](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3119) ([@cbowman0](https://github.com/cbowman0)) - Clarify `hub.config` can configure KubeSpawner and more [#3104](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3104) ([@JunaidChaudry](https://github.com/JunaidChaudry)) @@ -130,9 +148,9 @@ For a detailed list of Python dependencies in the `hub` Pod's Docker image, insp #### Contributors to this release -([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-06-04&type=c)) +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-07-05&type=c)) -@aaronjnewman ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-06-04&type=Issues)) | @alekseyolg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-06-04&type=Issues)) | @arunppsg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-06-04&type=Issues)) | @betatim ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-06-04&type=Issues)) | @bjornjorgensen ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornjorgensen+updated%3A2022-09-09..2023-06-04&type=Issues)) | @cbowman0 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-06-04&type=Issues)) | @choldgraf ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2022-09-09..2023-06-04&type=Issues)) | @ChristofKaufmann ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-06-04&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-06-04&type=Issues)) | @dasantonym ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-06-04&type=Issues)) | @DeepSkyWonder ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-06-04&type=Issues)) | @ebebpl ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-06-04&type=Issues)) | @HoseonRyu ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-06-04&type=Issues)) | @IceS2 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-06-04&type=Issues)) | @JunaidChaudry ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-06-04&type=Issues)) | @jupyterhub-bot ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2022-09-09..2023-06-04&type=Issues)) | @kanor1306 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-06-04&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-06-04&type=Issues)) | @mathbunnyru ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amathbunnyru+updated%3A2022-09-09..2023-06-04&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-06-04&type=Issues)) | @pnasrat ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-06-04&type=Issues)) | @Uular ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-06-04&type=Issues)) | @xcompass ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-06-04&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-06-04&type=Issues)) +[@aaronjnewman](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-07-05&type=Issues) | [@alekseyolg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-07-05&type=Issues) | [@arunppsg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-07-05&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-07-05&type=Issues) | [@cbowman0](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-07-05&type=Issues) | [@ChristofKaufmann](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-07-05&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-07-05&type=Issues) | [@dasantonym](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-07-05&type=Issues) | [@DeepSkyWonder](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-07-05&type=Issues) | [@ebebpl](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-07-05&type=Issues) | [@HoseonRyu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-07-05&type=Issues) | [@IceS2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-07-05&type=Issues) | [@JunaidChaudry](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-07-05&type=Issues) | [@kanor1306](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-07-05&type=Issues) | [@LucasVanHaaren](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ALucasVanHaaren+updated%3A2022-09-09..2023-07-05&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-07-05&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-07-05&type=Issues) | [@pnasrat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-07-05&type=Issues) | [@Uular](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-07-05&type=Issues) | [@xcompass](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-07-05&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-07-05&type=Issues) ## 2.0 From 9a62a78cc8d6e1062c0554fea6f5f96ed724ea45 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 14:11:54 +0200 Subject: [PATCH 768/898] docs: fix broken link --- docs/source/administrator/authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 810c3728c3..7ae7f34a69 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -136,7 +136,7 @@ require an OAuth2 _client id_ and _client secret_. For details on how to acquire a client id and client secret, please refer to [oauthenticator's -documentation](https://oauthenticator.readthedocs.io/en/stable/getting-started.html). +documentation](https://oauthenticator.readthedocs.io/en/stable/tutorials/general-setup.html). #### GitHub From ece0bf7cef1b7f672081c6c72bdbbbb14c815906 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 14:23:31 +0200 Subject: [PATCH 769/898] docs: fix incorrect version for nativeauthenticator --- docs/source/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index f9d4dbe06a..fabefd2372 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -63,7 +63,7 @@ to 3.0.0-beta.2. | [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.0 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.6.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | -| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.1 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | +| [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [tmpauthenticator](https://github.com/jupyterhub/tmpauthenticator) | 0.6 | 1.0.0 | [Changelog](https://github.com/jupyterhub/tmpauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.2.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | | [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.3 | 4.5.5 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | From a2b6b20f59417280124194115d325b2d1912a7f8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 15:08:28 +0200 Subject: [PATCH 770/898] Bump to 3.0.0-beta.2 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 77a818e1ad..062d6e842e 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0-beta.1" + baseVersion: "3.0.0-beta.2" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 76391c821c..6d65b33d04 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0-beta.1" +current = "3.0.0-beta.2" # match our prerelease prefixes # -alpha.1 From fe344e7a052e90571a903f4f28ff68d1f3402f69 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 5 Jul 2023 19:36:49 +0000 Subject: [PATCH 771/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 118460d0f2..61f04bfeaa 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -106,7 +106,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.0 +oauthenticator==16.0.1 # via -r requirements.in oauthlib==3.2.2 # via @@ -174,7 +174,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.17 +sqlalchemy==2.0.18 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 03ab0c2567..efd7cd5cfa 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -6,7 +6,7 @@ # alembic==1.11.1 # via jupyterhub -anyio==3.7.0 +anyio==3.7.1 # via jupyter-server argon2-cffi==21.3.0 # via @@ -285,7 +285,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.17 +sqlalchemy==2.0.18 # via # alembic # jupyterhub From 6e5828be533265f9b44ab9d2062b3eade0fe7887 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 23:22:35 +0200 Subject: [PATCH 772/898] docs: configure intersphinx to enable links based on sphinx object inventory --- docs/source/conf.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8c8f86dccb..5eba916402 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -24,6 +24,7 @@ extensions = [ "myst_parser", "sphinx_copybutton", + "sphinx.ext.intersphinx", "sphinx.ext.mathjax", "sphinxext.opengraph", "sphinxext.rediraffe", @@ -158,6 +159,29 @@ def parse_schema(d, md=[], depth=0, pre=""): f.write("\n".join(reference_md)) +# -- Options for intersphinx extension --------------------------------------- +# ref: https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#configuration +# +# The extension makes us able to link like to other projects like below. +# +# rST - :external:py:class:`tornado.httpclient.AsyncHTTPClient` +# MyST - {external:py:class}`tornado.httpclient.AsyncHTTPClient` +# +# To see what we can link to, do the following where "objects.inv" is appended +# to the sphinx based website: +# +# python -m sphinx.ext.intersphinx https://jupyterhub.readthedocs.io/en/stable/objects.inv +# +intersphinx_mapping = { + "jupyterhub": ("https://jupyterhub.readthedocs.io/en/stable/", None), + "oauthenticator": ("https://oauthenticator.readthedocs.io/en/stable/", None), +} + +# intersphinx_disabled_reftypes set based on recommendation in +# https://docs.readthedocs.io/en/stable/guides/intersphinx.html#using-intersphinx +intersphinx_disabled_reftypes = ["*"] + + # -- Options for HTML output ------------------------------------------------- # ref: https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output # From 4037245e2a9c965cd6ddf8acba522313b4d4420f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 5 Jul 2023 23:23:10 +0200 Subject: [PATCH 773/898] docs: let auth docs link to other projects docs --- docs/source/administrator/authentication.md | 190 +++++++++++++------- 1 file changed, 124 insertions(+), 66 deletions(-) diff --git a/docs/source/administrator/authentication.md b/docs/source/administrator/authentication.md index 7ae7f34a69..8a410c4c21 100644 --- a/docs/source/administrator/authentication.md +++ b/docs/source/administrator/authentication.md @@ -3,44 +3,45 @@ # Authentication and authorization Authentication is about identity, while _authorization_ is about permissions. In -this section you will learn how to configure both. As an example, you can -configure authentication using GitHub accounts and restrict what users are -authorized based on membership of a GitHub organization. +this section you will learn how to configure both by choosing and configuring a +_JupyterHub Authenticator class_. -Before configuring this, you should have [setup HTTPS](https). +As an example, you can configure JupyterHub to delegate authentication and +authorization to the GitHubOAuthenticator. It enable users to login with GitHub +accounts, where perhaps only a few specific users and other users users part of +a specific GitHub organization is allowed access. + +Before configuring authentication with an external identity provider, you must +have [setup HTTPS](https). ## Useful understanding ### Authenticator classes -Z2JH defaults to a [DummyAuthenticator](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.DummyAuthenticator) -that allows anyone to login with any username and password. -This should only be used for testing purposes. +By default a Z2JH deployment use the +{external:py:class}`jupyterhub.auth.DummyAuthenticator` JupyterHub authenticator +class that allows anyone to login with any username and password. This should +only be used for initial testing purposes. -To use other sources of authentication, choose _one_ [_authenticator -class_](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html) to use. -Several such classes are already available in the hub image through [installed -Python -packages](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt). +You should decide on a [jupyterhub authenticator class] to use. Several such +classes are available in the hub image through [installed Python packages], and +a few of them are described below. -JupyterHub provides a base class, -[`Authenticator`](https://github.com/jupyterhub/jupyterhub/blob/HEAD/jupyterhub/auth.py), -that all other authenticator classes are supposed to derive from. By configuring -this base class, we influence the behavior of the derived class as well. +[authenticator class]: https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html +[installed python packages]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/HEAD/images/hub/requirements.txt ### The configuration system -We configure JupyterHub to use our chosen authenticator class and the -authenticator class itself through this Helm chart's +First we should configure JupyterHub to use our chosen authenticator class and +the authenticator class itself through this Helm chart's [`hub.config`](schema_hub.config) configuration. ## General configuration -As all authenticator classes derive from the `Authenticator` base class, they -share some configuration options. Below are some common configuration options, -but please refer to the official [configuration -reference](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html) for more -details. +As all authenticator classes derive from the +{external:py:class}`jupyterhub.auth.Authenticator` base class, they share some +configuration options. Below are some common configuration options from the base +class. ### [allowed_users](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.Authenticator.allowed_users) / [admin_users](https://jupyterhub.readthedocs.io/en/stable/reference/api/auth.html#jupyterhub.auth.LocalAuthenticator.admin_users) @@ -129,10 +130,10 @@ documentation. ### OAuth2 based authentication -JupyterHub's [oauthenticator](https://github.com/jupyterhub/oauthenticator) has -support for enabling your users to authenticate via a third-party OAuth2 -_identity provider_ such as GitHub, Google, and CILogon. All of these will -require an OAuth2 _client id_ and _client secret_. +JupyterHub's [oauthenticator](https://github.com/jupyterhub/oauthenticator) +project has support for enabling your users to authenticate via a third-party +OAuth2 _identity provider_ such as GitHub, Google, and CILogon. All of these +will require an OAuth2 _client id_ and _client secret_. For details on how to acquire a client id and client secret, please refer to [oauthenticator's @@ -140,6 +141,17 @@ documentation](https://oauthenticator.readthedocs.io/en/stable/tutorials/general #### GitHub +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [GitHubOAuthenticator documentation]. + +[githuboauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/github.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + GitHub is the largest hosting service for git repositories. It is free to create an account at GitHub, and relatively straightforward to set up OAuth credentials so that users can authenticate with their GitHub username/password. @@ -203,6 +215,17 @@ For details about GitHub scopes, see [GitHub's documentation](https://docs.githu #### Google +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [GoogleOAuthenticator documentation]. + +[googleoauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/google.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + Google authentication is used by many universities (it is part of "G Suite"). If your institution is a [G Suite customer](https://workspace.google.com) that @@ -252,6 +275,17 @@ users which account they are using to login. #### CILogon +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [CILogonOAuthenticator documentation]. + +[ciLogonoauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/cilogon.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + Please see CyberInfrastructure Logon's [website](https://www.cilogon.org) for more information about what kind of identity is managed by CILogon. @@ -266,18 +300,18 @@ hub: authenticator_class: cilogon ``` -Based on [this -caveat](https://github.com/jupyterhub/oauthenticator/blob/6f239bebecbb3fb0242de7f753ae1c93ed101340/oauthenticator/cilogon.py#L5-L14), -you may need to also set the following. +#### Globus + +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [GlobusOAuthenticator documentation]. -```yaml -hub: - config: - CILogonOAuthenticator: - username_claim: email -``` +[globusoauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/globus.html -#### Globus +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` Globus Auth is a foundational identity and access management platform service designed to address unique needs of the science and engineering @@ -300,6 +334,17 @@ hub: #### Azure Active Directory +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [AzureAdOAuthenticator documentation]. + +[azureadoauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/azuread.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + [Azure Active Directory](https://learn.microsoft.com/en-us/azure/active-directory/) is an identity provider from Microsoft Azure. Apart from needing a OAuth2 _client id_ and _client secret_, you will also need a _tenant id_. @@ -318,6 +363,17 @@ hub: #### Auth0 +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [Auth0OAuthenticator documentation]. + +[auth0oauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/auth0.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + [Auth0](https://auth0.com/) is a commercial provider of identity management. ```yaml @@ -327,20 +383,27 @@ hub: client_id: client-id-from-auth0-here client_secret: client-secret-from-auth0-here oauth_callback_url: https://your-jupyterhub-domain/hub/oauth_callback + auth0_domain: prod-8ua-1yy9.auth0.com scope: - openid - email - auth0_subdomain: prod-8ua-1yy9 - Authenticator: - admin_users: - - devops@example.com - auto_login: true JupyterHub: authenticator_class: auth0 ``` #### GenericOAuthenticator - OpenID Connect +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [GenericOAuthenticator documentation]. + +[genericoauthenticator documentation]: https://oauthenticator.readthedocs.io/en/latest/tutorials/provider-specific-setup/providers/generic.html + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + [OpenID Connect](https://openid.net/connect) is an identity layer on top of the OAuth 2.0 protocol, implemented by [various servers and services](https://openid.net/certified-open-id-developer-tools/). While OpenID @@ -348,33 +411,17 @@ Connect endpoint discovery is not supported by oauthentiator, you can still configure JupyterHub to authenticate with OpenID Connect providers by specifying all endpoints in the GenericOAuthenticator class. -##### Auth0 +##### KeyCloak -Below is an example on how you can configure the GenericOAuthenticator to -authenticate against Auth0. +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [GenericOAuthenticator documentation]. -```yaml -hub: - config: - GenericOAuthenticator: - client_id: your-client-id - client_secret: your-client-secret - oauth_callback_url: https://your-jupyterhub-domain/hub/oauth_callback - authorize_url: https://your-domain.us.auth0.com/authorize - token_url: https://your-domain.us.auth0.com/oauth/token - userdata_url: https://your-domain.us.auth0.com/userinfo - scope: - - openid - - name - - profile - - email - username_key: name - JupyterHub: - authenticator_class: generic-oauth +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. ``` -##### KeyCloak - [KeyCloak](https://www.keycloak.org) is an open source based provider of identity management that you can host yourself. Below is an example on how you can configure the GenericOAuthenticator class to authenticate against a KeyCloak @@ -394,7 +441,7 @@ hub: token_url: https://${host}/realms/${realm}/protocol/openid-connect/token userdata_url: https://${host}/realms/${realm}/protocol/openid-connect/userinfo login_service: keycloak - username_key: preferred_username + username_claim: preferred_username userdata_params: state: state # In order to use keycloak client's roles as authorization layer @@ -409,6 +456,17 @@ hub: ### LDAP and Active Directory +```{warning} +This documentation may not have been updated recently. Due to that, please only use this +_as a complement_ to the official [LDAPAuthenticator documentation]. + +[ldapauthenticator documentation]: https://github.com/jupyterhub/ldapauthenticator#readme + +Going onwards, the goal is to ensure we have good documentation in the +OAuthenticator project and reference that instead of maintaining similar +documentation in this project also. +``` + JupyterHub supports LDAP and Active Directory authentication. Read the [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) documentation for a full explanation of the available parameters. From 1d36598417dedc43099d7d3deb7bced0eda18f56 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 6 Jul 2023 21:22:28 +0200 Subject: [PATCH 774/898] Update oauthenticator from 16.0.1 to 16.0.2 --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 61f04bfeaa..421d09a0d2 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -106,7 +106,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.1 +oauthenticator==16.0.2 # via -r requirements.in oauthlib==3.2.2 # via From 33cc348a2a00855dd00fdd575232a488c44d12d8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 6 Jul 2023 21:29:51 +0200 Subject: [PATCH 775/898] Add changelog for 3.0.0-beta.3 --- docs/source/changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index fabefd2372..6e4bd2c15c 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,15 +14,14 @@ changes in pull requests], this list should be updated. ## 3.0 -### 3.0.0-beta.2 - 2023-07-05 +### 3.0.0-beta.3 - 2023-07-06 This is a beta release for testing before the 3.0.0 release. ```{warning} Since 3.0.0-beta.1 release 2023-06-12, another breaking change was made by upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the -[OAuthenticator changelog]'s breaking changes before upgrading from 3.0.0-beta.1 -to 3.0.0-beta.2. +[OAuthenticator changelog]'s breaking changes before upgrading from 3.0.0-beta.1. ``` #### Breaking changes @@ -40,7 +39,7 @@ to 3.0.0-beta.2. configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. -- OAuthenticator 15.1.0 is upgraded to 16.0.0. +- OAuthenticator 15.1.0 is upgraded to 16.0.2. - If you are using a JupyterHub Authenticator class from this project, please read to the [OAuthenticator changelog]'s breaking changes before upgrading this Helm chart. @@ -60,7 +59,7 @@ to 3.0.0-beta.2. | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | | [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.0 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.2 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.6.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | @@ -107,6 +106,7 @@ what changes between tagged versions. #### Documentation improvements +- docs: let auth docs link to authenticator specific docs [#3151](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3151) ([@consideRatio](https://github.com/consideRatio)) - Enhance keycloak configuration example [#3142](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3142) ([@LucasVanHaaren](https://github.com/LucasVanHaaren)) - Show default value in configuration reference [#3138](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3138) ([@manics](https://github.com/manics)) - Helm chart url has changed [#3122](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3122) ([@manics](https://github.com/manics)) From d3f1723eab7cf3cf737ce416e71321ec8c01a922 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 6 Jul 2023 21:59:03 +0200 Subject: [PATCH 776/898] Bump to 3.0.0-beta.3 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 062d6e842e..add277c6cd 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0-beta.2" + baseVersion: "3.0.0-beta.3" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 6d65b33d04..21fc02759b 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0-beta.2" +current = "3.0.0-beta.3" # match our prerelease prefixes # -alpha.1 From abf210aac7bb1b9de4824b858c359bbdc716f4f2 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 8 Jul 2023 15:49:01 +0000 Subject: [PATCH 777/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 19 ++++++++++++----- images/singleuser-sample/requirements.txt | 25 +++++++++++++++-------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 421d09a0d2..4f9346ca8b 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -18,6 +18,7 @@ attrs==23.1.0 # via # aiohttp # jsonschema + # referencing bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator @@ -30,7 +31,7 @@ certipy==0.1.3 # via jupyterhub cffi==1.15.1 # via cryptography -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via # aiohttp # requests @@ -56,10 +57,12 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.17.3 +jsonschema==4.18.0 # via # jupyter-telemetry # oauthenticator +jsonschema-specifications==2023.6.1 + # via jsonschema jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==4.0.1 @@ -106,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.2 +oauthenticator==16.0.3 # via -r requirements.in oauthlib==3.2.2 # via @@ -139,8 +142,6 @@ pymysql==1.1.0 # via -r requirements.in pyopenssl==23.2.0 # via certipy -pyrsistent==0.19.3 - # via jsonschema python-dateutil==2.8.2 # via # jupyterhub @@ -154,6 +155,10 @@ pyyaml==6.0 # via # jupyterhub-kubespawner # kubernetes-asyncio +referencing==0.29.1 + # via + # jsonschema + # jsonschema-specifications requests==2.31.0 # via # jupyterhub @@ -162,6 +167,10 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth +rpds-py==0.8.10 + # via + # jsonschema + # referencing ruamel-yaml==0.17.32 # via # jupyter-telemetry diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index efd7cd5cfa..b6a051e87c 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -21,10 +21,12 @@ asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub -async-lru==2.0.2 +async-lru==2.0.3 # via jupyterlab attrs==23.1.0 - # via jsonschema + # via + # jsonschema + # referencing babel==2.12.1 # via jupyterlab-server backcall==0.2.0 @@ -41,7 +43,7 @@ cffi==1.15.1 # via # argon2-cffi-bindings # cryptography -charset-normalizer==3.1.0 +charset-normalizer==3.2.0 # via requests comm==0.1.3 # via ipykernel @@ -94,12 +96,14 @@ json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.17.3 +jsonschema[format-nongpl]==4.18.0 # via # jupyter-events # jupyter-telemetry # jupyterlab-server # nbformat +jsonschema-specifications==2023.6.1 + # via jsonschema jupyter-client==8.3.0 # via # ipykernel @@ -208,7 +212,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.8.0 +platformdirs==3.8.1 # via jupyter-core prometheus-client==0.17.0 # via @@ -234,8 +238,6 @@ pygments==2.15.1 # nbconvert pyopenssl==23.2.0 # via certipy -pyrsistent==0.19.3 - # via jsonschema python-dateutil==2.8.2 # via # arrow @@ -254,6 +256,10 @@ pyzmq==25.1.0 # jupyter-server # nbclassic # notebook +referencing==0.29.1 + # via + # jsonschema + # jsonschema-specifications requests==2.31.0 # via # jupyterhub @@ -266,6 +272,10 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events +rpds-py==0.8.10 + # via + # jsonschema + # referencing ruamel-yaml==0.17.32 # via jupyter-telemetry ruamel-yaml-clib==0.2.7 @@ -331,7 +341,6 @@ traitlets==5.9.0 typing-extensions==4.7.1 # via # alembic - # async-lru # sqlalchemy uri-template==1.3.0 # via jsonschema From a95833c5e57f117d986062467032226be814c74c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 8 Jul 2023 18:06:18 +0200 Subject: [PATCH 778/898] Update changelog for 3.0.0 release --- docs/source/changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6e4bd2c15c..633e3b6441 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -39,7 +39,7 @@ upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. -- OAuthenticator 15.1.0 is upgraded to 16.0.2. +- OAuthenticator 15.1.0 is upgraded to 16.0.3. - If you are using a JupyterHub Authenticator class from this project, please read to the [OAuthenticator changelog]'s breaking changes before upgrading this Helm chart. @@ -59,7 +59,7 @@ upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | | [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.2 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.3 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.6.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | From cbf71bdfd790579253e77a5c6da0f2a8374e4638 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Sat, 15 Jul 2023 08:36:00 +0000 Subject: [PATCH 779/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 8 ++++---- images/singleuser-sample/requirements.txt | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 4f9346ca8b..fb473a1d6b 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -35,7 +35,7 @@ charset-normalizer==3.2.0 # via # aiohttp # requests -cryptography==41.0.1 +cryptography==41.0.2 # via # pyjwt # pyopenssl @@ -43,7 +43,7 @@ escapism==1.0.1 # via # jupyterhub-kubespawner # jupyterhub-ltiauthenticator -frozenlist==1.3.3 +frozenlist==1.4.0 # via # aiohttp # aiosignal @@ -57,7 +57,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.18.0 +jsonschema==4.18.3 # via # jupyter-telemetry # oauthenticator @@ -123,7 +123,7 @@ packaging==23.1 # via jupyterhub pamela==1.1.0 # via jupyterhub -prometheus-client==0.17.0 +prometheus-client==0.17.1 # via jupyterhub psycopg2==2.9.6 # via -r requirements.in diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index b6a051e87c..f8576fcc6e 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -47,7 +47,7 @@ charset-normalizer==3.2.0 # via requests comm==0.1.3 # via ipykernel -cryptography==41.0.1 +cryptography==41.0.2 # via pyopenssl debugpy==1.6.7 # via ipykernel @@ -96,7 +96,7 @@ json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.18.0 +jsonschema[format-nongpl]==4.18.3 # via # jupyter-events # jupyter-telemetry @@ -140,7 +140,7 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==4.0.1 # via -r requirements.in -jupyterlab==4.0.2 +jupyterlab==4.0.3 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert @@ -170,7 +170,7 @@ nbconvert==7.6.0 # jupyter-server # nbclassic # notebook -nbformat==5.9.0 +nbformat==5.9.1 # via # jupyter-server # nbclassic @@ -214,7 +214,7 @@ pickleshare==0.7.5 # via ipython platformdirs==3.8.1 # via jupyter-core -prometheus-client==0.17.0 +prometheus-client==0.17.1 # via # jupyter-server # jupyterhub From 8b9de2011a57b1d5e93b024ba5db69b89ab6021b Mon Sep 17 00:00:00 2001 From: Adam Gray <127873640+monoakg@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:22:05 +0100 Subject: [PATCH 780/898] Add 'enabled' as top level key --- jupyterhub/values.schema.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 14aa32b046..ffeb8fc962 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -27,6 +27,11 @@ required: - rbac - global properties: + enabled: + type: boolean + description: | + enabled allows you to enable or disable jupyterhub using the preffered method + if it is included as a dependency in a parent helm chart fullnameOverride: type: [string, "null"] description: | From c8cb28e565a734406de4a2fac973cdfcae0ec0ee Mon Sep 17 00:00:00 2001 From: Adam Gray <127873640+monoakg@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:32:05 +0100 Subject: [PATCH 781/898] Include enable in values.yaml --- jupyterhub/values.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 3fc7bbc43d..751c06334f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -3,6 +3,9 @@ fullnameOverride: "" nameOverride: +# enable or disable jupyterhub if it is included as a dependency in a parent helm chart +enabled: + # custom can contain anything you want to pass to the hub pod, as all passed # Helm template values will be made available there. custom: {} From 5574ca6cef1fdac3eafb9699b75443f6489f7ac1 Mon Sep 17 00:00:00 2001 From: Adam Gray <127873640+monoakg@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:36:35 +0100 Subject: [PATCH 782/898] Allow null value for enabled --- jupyterhub/values.schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index ffeb8fc962..4a1ac1b6df 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -28,7 +28,7 @@ required: - global properties: enabled: - type: boolean + type: [boolean, "null"] description: | enabled allows you to enable or disable jupyterhub using the preffered method if it is included as a dependency in a parent helm chart From ee2acacc8f0e793f7175d71f84cd11199c5f89b2 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 20 Jul 2023 05:09:16 +0000 Subject: [PATCH 783/898] Update kube-scheduler version from v1.26.6 to v1.26.7 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 3fc7bbc43d..d522539dc9 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -506,7 +506,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.6" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.7" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 7e07a258b7e400694369cf1cf9652ce12faf2b78 Mon Sep 17 00:00:00 2001 From: Adam Gray <127873640+monoakg@users.noreply.github.com> Date: Sat, 22 Jul 2023 15:51:08 +0100 Subject: [PATCH 784/898] Update jupyterhub/values.schema.yaml Co-authored-by: Simon Li --- jupyterhub/values.schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 4a1ac1b6df..c3d6674d6d 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -30,7 +30,7 @@ properties: enabled: type: [boolean, "null"] description: | - enabled allows you to enable or disable jupyterhub using the preffered method + enabled allows you to enable or disable jupyterhub using the preferred method if it is included as a dependency in a parent helm chart fullnameOverride: type: [string, "null"] From a57beab88885d9e2a165fa6d3cc4164cb0f4945b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 23 Jul 2023 09:53:58 +0200 Subject: [PATCH 785/898] Apply suggestions from code review --- jupyterhub/values.schema.yaml | 5 +++-- jupyterhub/values.yaml | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index c3d6674d6d..f2756d8880 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -30,8 +30,9 @@ properties: enabled: type: [boolean, "null"] description: | - enabled allows you to enable or disable jupyterhub using the preferred method - if it is included as a dependency in a parent helm chart + `enabled` is ignored by the jupyterhub chart itself, but a chart depending + on the jupyterhub chart conditionally can make use this config option as + the condition. fullnameOverride: type: [string, "null"] description: | diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 751c06334f..c50d2a98e3 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -3,7 +3,9 @@ fullnameOverride: "" nameOverride: -# enable or disable jupyterhub if it is included as a dependency in a parent helm chart +# enabled is ignored by the jupyterhub chart itself, but a chart depending on +# the jupyterhub chart conditionally can make use this config option as the +# condition. enabled: # custom can contain anything you want to pass to the hub pod, as all passed From 934307748afe2a6f0ec1dd6d1784165416ce7c88 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 23 Jul 2023 10:04:01 +0200 Subject: [PATCH 786/898] ci: ignore unreliable link to traefik changelog in linkcheck --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 5eba916402..c2ce1a2196 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -217,6 +217,7 @@ def parse_schema(d, md=[], depth=0, pre=""): "https://your-domain-name.com", # example "https://kubernetes.io/docs/tutorials/kubernetes-basics/", # works "https://cloud.ibm.com/kubernetes/catalog/create", # works + "https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md", # works "https://portal.azure.com", # sign-in redirect noise "https://console.cloud.google.com", # sign-in redirect noise "https://console.developers.google.com", # sign-in redirect noise From 52d5e0cd7310546625ef402df908a54d357f559a Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 23 Jul 2023 10:50:09 +0100 Subject: [PATCH 787/898] Include shellcheck pre-commit in main pre-commit Disabled by default, run with `pre-commit run --all --hook-stage manual` --- .github/workflows/test-chart.yaml | 4 ++-- .pre-commit-config-shellcheck.yaml | 6 ------ .pre-commit-config.yaml | 8 ++++++++ 3 files changed, 10 insertions(+), 8 deletions(-) delete mode 100644 .pre-commit-config-shellcheck.yaml diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index e8aca1bcd1..2e284f0a83 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -38,8 +38,8 @@ jobs: - name: Install dependencies run: pip install pre-commit - - name: Run shellcheck linter - run: pre-commit run --all --config .pre-commit-config-shellcheck.yaml + - name: Run all stages including shellcheck (disabled by default) + run: pre-commit run --all --hook-stage manual lint_and_validate_rendered_templates: runs-on: ubuntu-22.04 diff --git a/.pre-commit-config-shellcheck.yaml b/.pre-commit-config-shellcheck.yaml deleted file mode 100644 index 3e0be7f9d7..0000000000 --- a/.pre-commit-config-shellcheck.yaml +++ /dev/null @@ -1,6 +0,0 @@ -# See .pre-commit-config.yaml for more details. -repos: - - repo: https://github.com/gruntwork-io/pre-commit - rev: v0.1.15 - hooks: - - id: shellcheck diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3eb666daa6..005c11936c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,6 +70,14 @@ repos: hooks: - id: flake8 + - repo: https://github.com/gruntwork-io/pre-commit + rev: v0.1.22 + hooks: + # This requires shellcheck to be installed manually so is disabled by default + - id: shellcheck + stages: + - manual + # pre-commit.ci config reference: https://pre-commit.ci/#configuration ci: autoupdate_schedule: monthly From dd967ccd39c07e21451a30776c76ef5590817ac0 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 25 Jul 2023 05:09:27 +0000 Subject: [PATCH 788/898] Update library/traefik version from v2.10.3 to v2.10.4 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 187abaa544..4433d27269 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -254,7 +254,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.10.3" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.10.4" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From c3c4e1214c529fc5d0d31dd55dc0a7d53db5db7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 23:09:42 +0000 Subject: [PATCH 789/898] build(deps): bump certifi from 2023.5.7 to 2023.7.22 in /images/hub Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.5.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2023.05.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index fb473a1d6b..f73d12bc85 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -23,7 +23,7 @@ bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2023.5.7 +certifi==2023.7.22 # via # kubernetes-asyncio # requests From d88f1973cdd6eafc93c19f6415a267a660b6fdca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jul 2023 23:10:59 +0000 Subject: [PATCH 790/898] build(deps): bump certifi in /images/singleuser-sample Bumps [certifi](https://github.com/certifi/python-certifi) from 2023.5.7 to 2023.7.22. - [Commits](https://github.com/certifi/python-certifi/compare/2023.05.07...2023.07.22) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index f8576fcc6e..1e0f5ac3bc 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -35,7 +35,7 @@ beautifulsoup4==4.12.2 # via nbconvert bleach==6.0.0 # via nbconvert -certifi==2023.5.7 +certifi==2023.7.22 # via requests certipy==0.1.3 # via jupyterhub From a05f6695ad5250a1263646d0b58cd81fa3c706d3 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 31 Jul 2023 05:12:51 +0000 Subject: [PATCH 791/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 3256d54059..1093630acd 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-05-29_05:12:47 +# VULN_SCAN_TIME=2023-07-31_05:12:49 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From eb14528489eccbb763108a219c9f7b208d8be6dc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 08:37:11 +0000 Subject: [PATCH 792/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.8.0 → v3.10.1](https://github.com/asottile/pyupgrade/compare/v3.8.0...v3.10.1) - [github.com/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) - [github.com/pre-commit/mirrors-prettier: v3.0.0-alpha.9-for-vscode → v3.0.0](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0-alpha.9-for-vscode...v3.0.0) - [github.com/PyCQA/flake8: 6.0.0 → 6.1.0](https://github.com/PyCQA/flake8/compare/6.0.0...6.1.0) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 005c11936c..8929796657 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.8.0 + rev: v3.10.1 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black args: @@ -54,7 +54,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0-alpha.9-for-vscode + rev: v3.0.0 hooks: - id: prettier @@ -66,7 +66,7 @@ repos: # Linting: Python code (see the file .flake8) - repo: https://github.com/PyCQA/flake8 - rev: "6.0.0" + rev: "6.1.0" hooks: - id: flake8 From 53f61c9231aa70d313db2fb754c7873fceeaf8cd Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 1 Aug 2023 19:58:12 +0200 Subject: [PATCH 793/898] Add and enable two egressAllowRules to ensure DNS access --- jupyterhub/templates/_helpers-netpol.tpl | 29 ++++++-- jupyterhub/values.schema.yaml | 71 +++++++++++++++++-- jupyterhub/values.yaml | 8 +++ tools/templates/lint-and-validate-values.yaml | 2 + 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 5adbd3d275..4bf2596f5e 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -30,20 +30,35 @@ {{- define "jupyterhub.networkPolicy.renderEgressRules" -}} {{- $root := index . 0 }} {{- $netpol := index . 1 }} -{{- if $netpol.egressAllowRules.dnsPortsPrivateIPs }} -# Allow outbound connections to the DNS port in the private IP ranges +{{- if or (or $netpol.egressAllowRules.dnsPortsCloudMetadataServer $netpol.egressAllowRules.dnsPortsKubeSystemNamespace) $netpol.egressAllowRules.dnsPortsPrivateIPs }} - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP to: + {{- if $netpol.egressAllowRules.dnsPortsCloudMetadataServer }} + # Allow outbound connections to DNS ports on the cloud metadata server + - ipBlock: + cidr: {{ $root.Values.singleuser.cloudMetadata.ip }}/32 + {{- end }} + {{- if $netpol.egressAllowRules.dnsPortsKubeSystemNamespace }} + # Allow outbound connections to DNS ports on pods in the kube-system + # namespace + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + {{- end }} + {{- if $netpol.egressAllowRules.dnsPortsPrivateIPs }} + # Allow outbound connections to DNS ports on destinations in the private IP + # ranges - ipBlock: cidr: 10.0.0.0/8 - ipBlock: cidr: 172.16.0.0/12 - ipBlock: cidr: 192.168.0.0/16 + {{- end }} {{- end }} {{- if $netpol.egressAllowRules.nonPrivateIPs }} @@ -53,7 +68,7 @@ cidr: 0.0.0.0/0 except: # As part of this rule, don't: - # - allow outbound connections to private IP + # - allow outbound connections to private IPs - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index f2756d8880..2c45084c40 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -667,6 +667,49 @@ properties: IP ranges but makes an exception for the cloud metadata server, leaving this as the definitive configuration to allow access to the cloud metadata server. + dnsPortsCloudMetadataServer: + type: boolean + description: | + Defaults to `true` for all network policies. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to the cloud metadata server + via port 53. + + Relying on this rule should go hand in hand with disabling + [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables). + + Known situations when this rule can be relevant: + + - In GKE clusters with Cloud DNS that is reached at the + cloud metadata server's non-private IP. + + ```{note} + This chart doesn't know how to identify the DNS server that + pods will rely on due to variations between how k8s clusters + have been setup. Due to that, multiple rules are enabled by + default to ensure DNS connectivity. + ``` + dnsPortsKubeSystemNamespace: + type: boolean + description: | + Defaults to `true` for all network policies. + + When enabled this rule allows the respective pod(s) to + establish outbound connections to pods in the kube-system + namespace via port 53. + + Known situations when this rule can be relevant: + + - GKE, EKS, AKS, and other clusters relying directly on + `kube-dns` or `coredns` pods in the `kube-system` namespace. + + ```{note} + This chart doesn't know how to identify the DNS server that + pods will rely on due to variations between how k8s clusters + have been setup. Due to that, multiple rules are enabled by + default to ensure DNS connectivity. + ``` dnsPortsPrivateIPs: type: boolean description: | @@ -675,10 +718,23 @@ properties: When enabled this rule allows the respective pod(s) to establish outbound connections to private IPs via port 53. - Note that we can't reliably identify the k8s internal DNS - server due to variations between k8s clusters. Due to that, - this rule which is critical for core functionality, can be - disabled for a more refined custom rule. + Known situations when this rule can be relevant: + + - GKE clusters relying on a DNS server indirectly via a a node + local DNS cache at an unknown private IP. + + ```{note} + This chart doesn't know how to identify the DNS server that + pods will rely on due to variations between how k8s clusters + have been setup. Due to that, multiple rules are enabled by + default to ensure DNS connectivity. + + ```{warning} + This rule is not expected to work in clusters relying on + Cilium to enforce the NetworkPolicy rules (includes GKE + clusters with Dataplane v2), this is due to a [known + limitation](https://github.com/cilium/cilium/issues/9209). + ``` nonPrivateIPs: type: boolean description: | @@ -713,6 +769,13 @@ properties: If possible, try to avoid setting this to true as it gives broad permissions that could be specified more directly via the [`.egress`](schema_singleuser.networkPolicy.egress). + + ```{warning} + This rule is not expected to work in clusters relying on + Cilium to enforce the NetworkPolicy rules (includes GKE + clusters with Dataplane v2), this is due to a [known + limitation](https://github.com/cilium/cilium/issues/9209). + ``` interNamespaceAccessLabels: enum: [accept, ignore] description: | diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 4433d27269..364af89847 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -107,6 +107,8 @@ hub: egress: [] egressAllowRules: cloudMetadataServer: true + dnsPortsCloudMetadataServer: true + dnsPortsKubeSystemNamespace: true dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: true @@ -231,6 +233,8 @@ proxy: egress: [] egressAllowRules: cloudMetadataServer: true + dnsPortsCloudMetadataServer: true + dnsPortsKubeSystemNamespace: true dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: true @@ -278,6 +282,8 @@ proxy: egress: [] egressAllowRules: cloudMetadataServer: true + dnsPortsCloudMetadataServer: true + dnsPortsKubeSystemNamespace: true dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: true @@ -354,6 +360,8 @@ singleuser: egress: [] egressAllowRules: cloudMetadataServer: false + dnsPortsCloudMetadataServer: true + dnsPortsKubeSystemNamespace: true dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: false diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 694f6f37ce..811d5ec818 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -177,6 +177,8 @@ hub: enabled: true egressAllowRules: cloudMetadataServer: true + dnsPortsCloudMetadataServer: true + dnsPortsKubeSystemNamespace: true dnsPortsPrivateIPs: true nonPrivateIPs: true privateIPs: false From 97e7acfb06d7c33b6c737a75eb95fab494f03dd4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 1 Aug 2023 20:14:00 +0200 Subject: [PATCH 794/898] docs: add/update versionadded notes --- jupyterhub/values.schema.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 2c45084c40..c56b076aaa 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -651,7 +651,6 @@ properties: ``` ```{versionadded} 2.0.0 - All `egressAllowRules` are new in JupyterHub Helm chart 2.0.0. ``` properties: cloudMetadataServer: @@ -690,6 +689,9 @@ properties: have been setup. Due to that, multiple rules are enabled by default to ensure DNS connectivity. ``` + + ```{versionadded} 3.0.0 + ``` dnsPortsKubeSystemNamespace: type: boolean description: | @@ -710,6 +712,9 @@ properties: have been setup. Due to that, multiple rules are enabled by default to ensure DNS connectivity. ``` + + ```{versionadded} 3.0.0 + ``` dnsPortsPrivateIPs: type: boolean description: | From cc00141885adb6dda92c4e0e29578631c5b6f5a6 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 2 Aug 2023 07:04:55 +0000 Subject: [PATCH 795/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 20 +++---- images/singleuser-sample/requirements.txt | 70 ++++++++++------------- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index f73d12bc85..89d916a012 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.8.4 +aiohttp==3.8.5 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp @@ -35,7 +35,7 @@ charset-normalizer==3.2.0 # via # aiohttp # requests -cryptography==41.0.2 +cryptography==41.0.3 # via # pyjwt # pyopenssl @@ -57,11 +57,11 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.18.3 +jsonschema==4.18.4 # via # jupyter-telemetry # oauthenticator -jsonschema-specifications==2023.6.1 +jsonschema-specifications==2023.7.1 # via jsonschema jupyter-telemetry==0.1.0 # via jupyterhub @@ -133,7 +133,7 @@ pycparser==2.21 # via cffi pycurl==7.45.2 # via -r requirements.in -pyjwt[crypto]==2.7.0 +pyjwt[crypto]==2.8.0 # via # -r requirements.in # jupyterhub-ltiauthenticator @@ -151,11 +151,11 @@ python-json-logger==2.0.7 # via jupyter-telemetry python-slugify==8.0.1 # via jupyterhub-kubespawner -pyyaml==6.0 +pyyaml==6.0.1 # via # jupyterhub-kubespawner # kubernetes-asyncio -referencing==0.29.1 +referencing==0.30.0 # via # jsonschema # jsonschema-specifications @@ -167,7 +167,7 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -rpds-py==0.8.10 +rpds-py==0.9.2 # via # jsonschema # referencing @@ -183,7 +183,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.18 +sqlalchemy==2.0.19 # via # alembic # jupyterhub @@ -212,7 +212,7 @@ typing-extensions==4.7.1 # via # alembic # sqlalchemy -urllib3==2.0.3 +urllib3==2.0.4 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 1e0f5ac3bc..086e800594 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -12,7 +12,6 @@ argon2-cffi==21.3.0 # via # jupyter-server # nbclassic - # notebook argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.2.3 @@ -21,7 +20,7 @@ asttokens==2.2.1 # via stack-data async-generator==1.10 # via jupyterhub -async-lru==2.0.3 +async-lru==2.0.4 # via jupyterlab attrs==23.1.0 # via @@ -47,7 +46,7 @@ charset-normalizer==3.2.0 # via requests comm==0.1.3 # via ipykernel -cryptography==41.0.2 +cryptography==41.0.3 # via pyopenssl debugpy==1.6.7 # via ipykernel @@ -57,7 +56,7 @@ defusedxml==0.7.1 # via nbconvert executing==1.2.0 # via stack-data -fastjsonschema==2.17.1 +fastjsonschema==2.18.0 # via nbformat fqdn==1.5.1 # via jsonschema @@ -68,20 +67,17 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.24.0 +ipykernel==6.25.0 # via # jupyterlab # nbclassic - # notebook ipython==8.14.0 # via ipykernel ipython-genutils==0.2.0 - # via - # nbclassic - # notebook + # via nbclassic isoduration==20.11.0 # via jsonschema -jedi==0.18.2 +jedi==0.19.0 # via ipython jinja2==3.1.2 # via @@ -91,18 +87,17 @@ jinja2==3.1.2 # jupyterlab-server # nbclassic # nbconvert - # notebook json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.18.3 +jsonschema[format-nongpl]==4.18.4 # via # jupyter-events # jupyter-telemetry # jupyterlab-server # nbformat -jsonschema-specifications==2023.6.1 +jsonschema-specifications==2023.7.1 # via jsonschema jupyter-client==8.3.0 # via @@ -110,7 +105,6 @@ jupyter-client==8.3.0 # jupyter-server # nbclassic # nbclient - # notebook jupyter-core==5.3.1 # via # ipykernel @@ -121,8 +115,7 @@ jupyter-core==5.3.1 # nbclient # nbconvert # nbformat - # notebook -jupyter-events==0.6.3 +jupyter-events==0.7.0 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab @@ -133,6 +126,7 @@ jupyter-server==2.7.0 # jupyterlab-server # nbclassic # nbgitpuller + # notebook # notebook-shim jupyter-server-terminals==0.4.4 # via jupyter-server @@ -141,11 +135,15 @@ jupyter-telemetry==0.1.0 jupyterhub==4.0.1 # via -r requirements.in jupyterlab==4.0.3 - # via -r requirements.in + # via + # -r requirements.in + # notebook jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.23.0 - # via jupyterlab +jupyterlab-server==2.24.0 + # via + # jupyterlab + # notebook mako==1.2.4 # via alembic markupsafe==2.1.3 @@ -160,36 +158,32 @@ matplotlib-inline==0.1.6 mistune==3.0.1 # via nbconvert nbclassic==1.0.0 - # via - # -r requirements.in - # notebook + # via -r requirements.in nbclient==0.8.0 # via nbconvert -nbconvert==7.6.0 +nbconvert==7.7.3 # via # jupyter-server # nbclassic - # notebook -nbformat==5.9.1 +nbformat==5.9.2 # via # jupyter-server # nbclassic # nbclient # nbconvert - # notebook nbgitpuller==1.1.1 # via -r requirements.in -nest-asyncio==1.5.6 +nest-asyncio==1.5.7 # via # ipykernel # nbclassic - # notebook -notebook==6.5.4 +notebook==7.0.1 # via nbgitpuller notebook-shim==0.2.3 # via # jupyterlab # nbclassic + # notebook oauthlib==3.2.2 # via jupyterhub overrides==7.3.1 @@ -212,14 +206,13 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.8.1 +platformdirs==3.10.0 # via jupyter-core prometheus-client==0.17.1 # via # jupyter-server # jupyterhub # nbclassic - # notebook prompt-toolkit==3.0.39 # via ipython psutil==5.9.5 @@ -247,7 +240,7 @@ python-json-logger==2.0.7 # via # jupyter-events # jupyter-telemetry -pyyaml==6.0 +pyyaml==6.0.1 # via jupyter-events pyzmq==25.1.0 # via @@ -255,11 +248,11 @@ pyzmq==25.1.0 # jupyter-client # jupyter-server # nbclassic - # notebook -referencing==0.29.1 +referencing==0.30.0 # via # jsonschema # jsonschema-specifications + # jupyter-events requests==2.31.0 # via # jupyterhub @@ -272,7 +265,7 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rpds-py==0.8.10 +rpds-py==0.9.2 # via # jsonschema # referencing @@ -284,7 +277,6 @@ send2trash==1.8.2 # via # jupyter-server # nbclassic - # notebook six==1.16.0 # via # asttokens @@ -295,7 +287,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.18 +sqlalchemy==2.0.19 # via # alembic # jupyterhub @@ -306,7 +298,6 @@ terminado==0.17.1 # jupyter-server # jupyter-server-terminals # nbclassic - # notebook tinycss2==1.2.1 # via nbconvert tornado==6.3.2 @@ -337,14 +328,13 @@ traitlets==5.9.0 # nbclient # nbconvert # nbformat - # notebook typing-extensions==4.7.1 # via # alembic # sqlalchemy uri-template==1.3.0 # via jsonschema -urllib3==2.0.3 +urllib3==2.0.4 # via requests wcwidth==0.2.6 # via prompt-toolkit From d0e6bff5ffe89e4b9f654b07576423b2e4045c46 Mon Sep 17 00:00:00 2001 From: Wermeille Bastien Date: Fri, 4 Aug 2023 09:31:54 +0200 Subject: [PATCH 796/898] Add deprecation warning for `kube-lego` (https certificates) Add a deprecation warning message for the section about `kube-lego` as it has been deprecated in favor of `cert-manager`. --- docs/source/administrator/advanced.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/source/administrator/advanced.md b/docs/source/administrator/advanced.md index 30a505c0c6..9c2c47c460 100644 --- a/docs/source/administrator/advanced.md +++ b/docs/source/administrator/advanced.md @@ -44,6 +44,11 @@ ingress: ### Ingress and Automatic HTTPS with kube-lego & Let's Encrypt +```{warning} +`kube-lego` has been deprecated in favor of `cert-manager`. +See [cert-manager](https://github.com/cert-manager/cert-manager). +``` + When using an ingress object, the default automatic HTTPS support does not work. To have automatic fetch and renewal of HTTPS certificates, you must set it up yourself. From 5584cf7f2a34d15e69ffc3fce55209a22ab6401b Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 10 Aug 2023 09:36:20 +0000 Subject: [PATCH 797/898] Update jupyterhub from 4.0.1 to 4.0.2 --- images/hub/requirements.in | 2 +- images/hub/requirements.txt | 10 +++--- images/singleuser-sample/requirements.in | 2 +- images/singleuser-sample/requirements.txt | 37 +++++++++-------------- jupyterhub/Chart.yaml | 2 +- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/images/hub/requirements.in b/images/hub/requirements.in index f3b0e4a811..a40c90a731 100644 --- a/images/hub/requirements.in +++ b/images/hub/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.1 +jupyterhub==4.0.2 ## Authenticators jupyterhub-firstuseauthenticator>=1 diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 89d916a012..9d20e31651 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.5 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.11.1 +alembic==1.11.2 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -57,7 +57,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.18.4 +jsonschema==4.19.0 # via # jupyter-telemetry # oauthenticator @@ -65,7 +65,7 @@ jsonschema-specifications==2023.7.1 # via jsonschema jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.1 +jupyterhub==4.0.2 # via # -r requirements.in # jupyterhub-firstuseauthenticator @@ -125,7 +125,7 @@ pamela==1.1.0 # via jupyterhub prometheus-client==0.17.1 # via jupyterhub -psycopg2==2.9.6 +psycopg2==2.9.7 # via -r requirements.in pyasn1==0.5.0 # via ldap3 @@ -155,7 +155,7 @@ pyyaml==6.0.1 # via # jupyterhub-kubespawner # kubernetes-asyncio -referencing==0.30.0 +referencing==0.30.2 # via # jsonschema # jsonschema-specifications diff --git a/images/singleuser-sample/requirements.in b/images/singleuser-sample/requirements.in index 496675c93f..4666759bf0 100644 --- a/images/singleuser-sample/requirements.in +++ b/images/singleuser-sample/requirements.in @@ -7,7 +7,7 @@ # JupyterHub itself, update this version pinning by running the workflow # mentioned above. -jupyterhub==4.0.1 +jupyterhub==4.0.2 # UI jupyterlab diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 086e800594..5ae7ca3a88 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.11.1 +alembic==1.11.2 # via jupyterhub anyio==3.7.1 # via jupyter-server @@ -44,11 +44,11 @@ cffi==1.15.1 # cryptography charset-normalizer==3.2.0 # via requests -comm==0.1.3 +comm==0.1.4 # via ipykernel cryptography==41.0.3 # via pyopenssl -debugpy==1.6.7 +debugpy==1.6.7.post1 # via ipykernel decorator==5.1.1 # via ipython @@ -67,7 +67,7 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.25.0 +ipykernel==6.25.1 # via # jupyterlab # nbclassic @@ -91,7 +91,7 @@ json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.18.4 +jsonschema[format-nongpl]==4.19.0 # via # jupyter-events # jupyter-telemetry @@ -126,24 +126,19 @@ jupyter-server==2.7.0 # jupyterlab-server # nbclassic # nbgitpuller - # notebook # notebook-shim jupyter-server-terminals==0.4.4 # via jupyter-server jupyter-telemetry==0.1.0 # via jupyterhub -jupyterhub==4.0.1 +jupyterhub==4.0.2 + # via -r requirements.in +jupyterlab==4.0.4 # via -r requirements.in -jupyterlab==4.0.3 - # via - # -r requirements.in - # notebook jupyterlab-pygments==0.2.2 # via nbconvert jupyterlab-server==2.24.0 - # via - # jupyterlab - # notebook + # via jupyterlab mako==1.2.4 # via alembic markupsafe==2.1.3 @@ -171,22 +166,19 @@ nbformat==5.9.2 # nbclassic # nbclient # nbconvert -nbgitpuller==1.1.1 +nbgitpuller==1.2.0 # via -r requirements.in nest-asyncio==1.5.7 # via # ipykernel # nbclassic -notebook==7.0.1 - # via nbgitpuller notebook-shim==0.2.3 # via # jupyterlab # nbclassic - # notebook oauthlib==3.2.2 # via jupyterhub -overrides==7.3.1 +overrides==7.4.0 # via jupyter-server packaging==23.1 # via @@ -225,7 +217,7 @@ pure-eval==0.2.2 # via stack-data pycparser==2.21 # via cffi -pygments==2.15.1 +pygments==2.16.1 # via # ipython # nbconvert @@ -242,13 +234,13 @@ python-json-logger==2.0.7 # jupyter-telemetry pyyaml==6.0.1 # via jupyter-events -pyzmq==25.1.0 +pyzmq==25.1.1 # via # ipykernel # jupyter-client # jupyter-server # nbclassic -referencing==0.30.0 +referencing==0.30.2 # via # jsonschema # jsonschema-specifications @@ -309,7 +301,6 @@ tornado==6.3.2 # jupyterlab # nbclassic # nbgitpuller - # notebook # terminado traitlets==5.9.0 # via diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 7a7beefdc1..4e3180edde 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "4.0.1" +appVersion: "4.0.2" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org From b1569106f23e10ad5b4cb21c0fc962b4501b0875 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Aug 2023 18:45:41 +0200 Subject: [PATCH 798/898] maint: cleanup in development chart config files --- dev-config-local-chart-extra-config.yaml | 26 ------------------------ dev-config.yaml | 19 +++++++++++++++++ 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/dev-config-local-chart-extra-config.yaml b/dev-config-local-chart-extra-config.yaml index 8df40b769d..ba5b301188 100644 --- a/dev-config-local-chart-extra-config.yaml +++ b/dev-config-local-chart-extra-config.yaml @@ -8,29 +8,3 @@ # include this config and then pass --set hub.some-option=null to null it out # when it must not be passed, but that still triggers schema validation errors. # -hub: - # FIXME: move loadRoles to dev-config.yaml after 2.0.0 is released. - loadRoles: - test-scoped-access: - description: Used to JupyterHub 2.0.0+ RBAC scoped access, currently to the /hub/api/info endpoint via read:hub. - scopes: [read:hub] - services: [test-with-scoped-access] - test-role-with-explicit-name: - name: test-role-2 - description: Access to users' information and group membership - scopes: [users, groups] - users: [cyclops, gandalf] - services: [test] - groups: [] - -singleuser: - # FIXME: move resources to dev-config.yaml after 2.0.0 is released. - networkPolicy: - egressAllowRules: - nonPrivateIPs: false - networkTools: - # FIXME: move resources to dev-config.yaml after 2.0.0 is released. - resources: - requests: - memory: 0 - cpu: 0 diff --git a/dev-config.yaml b/dev-config.yaml index cd8795bb7d..1dc76d7dc5 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -34,6 +34,18 @@ proxy: hub: db: type: sqlite-memory + loadRoles: + test-scoped-access: + description: Used to JupyterHub 2.0.0+ RBAC scoped access, currently to the /hub/api/info endpoint via read:hub. + scopes: [read:hub] + services: [test-with-scoped-access] + test-role-with-explicit-name: + name: test-role-2 + description: Access to users' information and group membership + scopes: [users, groups] + users: [cyclops, gandalf] + services: [test] + groups: [] services: # The test service and its apiToken is used to make requests in our pytest # suite of tests. Note that it can be overridden by the hub.existingSecret, @@ -152,6 +164,8 @@ singleuser: networkPolicy: # For testing purposes in the test_spawn_netpol test, we override egress and # egressAllowRules.nonPrivateIPs to slim the egress rules to a minimum. + egressAllowRules: + nonPrivateIPs: false egress: - to: # jupyter.org has multiple IPs associated with it, among them are these @@ -160,6 +174,11 @@ singleuser: cidr: 104.21.25.233/32 # - ipBlock: # cidr: 172.67.134.225/32 + networkTools: + resources: + requests: + memory: 0 + cpu: 0 extraEnv: TEST_ENV_FIELDREF_TO_NAMESPACE: From fb0595dbf06025e6ec482e215c70328765c1eb83 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 2 Aug 2023 11:39:24 +0200 Subject: [PATCH 799/898] maint: restrict allowed config with blockWithIpTables, add misc docs --- docs/source/administrator/security.md | 32 +++++++++++++------ docs/source/changelog.md | 14 ++++++-- jupyterhub/files/hub/jupyterhub_config.py | 15 ++++++--- jupyterhub/templates/NOTES.txt | 9 ++++-- jupyterhub/templates/_helpers-netpol.tpl | 6 ++-- jupyterhub/values.schema.yaml | 14 ++++++-- tools/templates/lint-and-validate-values.yaml | 2 +- 7 files changed, 67 insertions(+), 25 deletions(-) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 849ee8f9f8..56edc67f56 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -279,23 +279,29 @@ only need one. (block-metadata-netpol)= -### Block metadata with a NetworkPolicy enforced by a NetworkPolicy controller +### Block cloud metadata API with a NetworkPolicy enforced by a NetworkPolicy controller -If you have _NetworkPolicy controller_ such as Calico in the Kubernetes cluster, -it will enforce the NetworkPolicy resource created by this chart -(`singleuser.networkPolicy.*`) that blocks user access to the metadata server. -We recommend relying on this approach if you you had a NetworkPolicy controller, -and then you can disable the other option. +If you have _NetworkPolicy controller_ such as Calico or Cilium in the +Kubernetes cluster, it will enforce the NetworkPolicy resource created by this +chart (`singleuser.networkPolicy.*`) that by default doesn't allow (and +therefore blocks) user access to the cloud metadata API exposed on a specific IP +(`169.254.169.254`). + +```{note} +If you have a NetworkPolicy controller, we recommend relying on it and setting +`singleuser.cloudMetadata.blockWithIptables` to `false`. +``` (block-metadata-iptables)= -### Block metadata with a privileged initContainer running `iptables` +### Block cloud metadata API with a privileged initContainer running `iptables` -If you can't rely on the NetworkPolicy approach to block access to the metadata -server, we suggest relying on this option. When +If you can't rely on the NetworkPolicy approach to block access to the cloud +metadata API, we suggest relying on this option instead. When `singleuser.cloudMetadata.blockWithIptables` is true as it is by default, an `initContainer` is added to the user pods. It will run with elevated privileges -and use the `iptables` command line tool to block access to the metadata server. +and use the `iptables` command line tool to block all network access to the +cloud metadata server. ```yaml # default configuration @@ -305,6 +311,12 @@ singleuser: ip: 169.254.169.254 ``` +```{versionchanged} 3.0.0 +This configuration is not allowed to be configured true at the same time as +[`singleuser.networkPolicy.egressAllowRules.cloudMetadataServer`](schema_singleuser.networkPolicy.egressAllowRules.cloudMetadataServer) +to avoid an ambiguous configuration. +``` + (netpol)= ## Kubernetes Network Policies diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 633e3b6441..7c05ea0f5f 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -19,9 +19,14 @@ changes in pull requests], this list should be updated. This is a beta release for testing before the 3.0.0 release. ```{warning} -Since 3.0.0-beta.1 release 2023-06-12, another breaking change was made by +Since 3.0.0-beta.1 released 2023-06-12, another breaking change was made by upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the -[OAuthenticator changelog]'s breaking changes before upgrading from 3.0.0-beta.1. +[OAuthenticator changelog]'s breaking changes before upgrading from +3.0.0-beta.1. + +Since 3.0.0-beta.3 released 2023-07-06, default networking rules related to +establishing connections to DNS ports has changed slightly as documented in the +breaking changes below. ``` #### Breaking changes @@ -47,6 +52,11 @@ upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the - If you are using this JupyterHub Authenticator class, please read to the [TmpAuthenticator changelog]'s breaking changes before upgrading this Helm chart. +- Predefined NetworkPolicy egress allow rules + [`dnsPortsCloudMetadataServer`](schema_hub.networkPolicy.egressAllowRules.dnsPortsCloudMetadataServer) + and + [`dnsPortsKubeSystemNamespace`](schema_hub.networkPolicy.egressAllowRules.dnsPortsKubeSystemNamespace) + are introduced and enabled by default for the chart's NetworkPolicy resources. [jupyterhub changelog]: https://jupyterhub.readthedocs.io/en/stable/changelog.html [kubespawner changelog]: https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index e30be9aa34..7047173b79 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -419,23 +419,28 @@ def camelCaseify(s): set_config_if_not_none(c.Spawner, "default_url", "singleuser.defaultUrl") -cloud_metadata = get_config("singleuser.cloudMetadata", {}) +cloud_metadata = get_config("singleuser.cloudMetadata") if cloud_metadata.get("blockWithIptables") == True: # Use iptables to block access to cloud metadata by default network_tools_image_name = get_config("singleuser.networkTools.image.name") network_tools_image_tag = get_config("singleuser.networkTools.image.tag") network_tools_resources = get_config("singleuser.networkTools.resources") + ip = cloud_metadata["ip"] ip_block_container = client.V1Container( name="block-cloud-metadata", image=f"{network_tools_image_name}:{network_tools_image_tag}", command=[ "iptables", - "-A", + "--append", "OUTPUT", - "-d", - cloud_metadata.get("ip", "169.254.169.254"), - "-j", + "--protocol", + "tcp", + "--destination", + ip, + "--destination-port", + "80", + "--jump", "DROP", ], security_context=client.V1SecurityContext( diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index e9a4edfcd4..518ef50de3 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -92,7 +92,7 @@ {{- /* - Warnings for likely misconfiguration + Warnings for likely misconfigurations */}} {{- if and (not .Values.scheduling.podPriority.enabled) (and .Values.scheduling.userPlaceholder.enabled .Values.scheduling.userPlaceholder.replicas) }} @@ -114,7 +114,7 @@ {{- /* - Breaking changes. + Breaking changes and failures for likely misconfigurations. */}} {{- $breaking := "" }} @@ -148,6 +148,11 @@ {{- end }} +{{- if and .Values.singleuser.cloudMetadata.blockWithIptables (and .Values.singleuser.networkPolicy.enabled .Values.singleuser.networkPolicy.egressAllowRules.cloudMetadataServer) }} +{{- $breaking = print $breaking "\n\nCHANGED: singleuser.cloudMetadata.blockWithIptables must as of version 3.0.0 not be configured together with singleuser.networkPolicy.egressAllowRules.cloudMetadataServer as it leads to an ambiguous configuration." }} +{{- end }} + + {{- if $breaking }} {{- fail (print $breaking_title $breaking "\n\n") }} {{- end }} diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl index 4bf2596f5e..006f633227 100644 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -67,12 +67,12 @@ - ipBlock: cidr: 0.0.0.0/0 except: - # As part of this rule, don't: - # - allow outbound connections to private IPs + # As part of this rule: + # - don't allow outbound connections to private IPs - 10.0.0.0/8 - 172.16.0.0/12 - 192.168.0.0/16 - # - allow outbound connections to the cloud metadata server + # - don't allow outbound connections to the cloud metadata server - {{ $root.Values.singleuser.cloudMetadata.ip }}/32 {{- end }} diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index c56b076aaa..6f9dd314ff 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -666,6 +666,13 @@ properties: IP ranges but makes an exception for the cloud metadata server, leaving this as the definitive configuration to allow access to the cloud metadata server. + + ```{versionchanged} 3.0.0 + This configuration is not allowed to be configured true at the + same time as + [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables) + to avoid an ambiguous configuration. + ``` dnsPortsCloudMetadataServer: type: boolean description: | @@ -675,8 +682,10 @@ properties: establish outbound connections to the cloud metadata server via port 53. - Relying on this rule should go hand in hand with disabling - [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables). + Relying on this rule for the singleuser config should go hand + in hand with disabling + [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables) + to avoid an ambiguous configuration. Known situations when this rule can be relevant: @@ -2201,6 +2210,7 @@ properties: cloudMetadata: type: object additionalProperties: false + required: [blockWithIptables, ip] description: | Please refer to dedicated section in [the Helm chart documentation](block-metadata-iptables) for more information about diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 811d5ec818..0db90333c7 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -404,7 +404,7 @@ singleuser: networkPolicy: enabled: true egressAllowRules: - cloudMetadataServer: true + cloudMetadataServer: false dnsPortsPrivateIPs: true nonPrivateIPs: false privateIPs: false From f9b268e59f3e986fc4c70f45ee4b696a17d8f3b6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 10 Aug 2023 20:01:28 +0200 Subject: [PATCH 800/898] ci: test upgrades from 2.0.0 release instead of 1.2.0 release --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 2e284f0a83..90e0593c4c 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -187,7 +187,7 @@ jobs: test: upgrade # We're testing hub.db.upgrade with PostgreSQL so this version must be old # enough to require a DB upgrade - upgrade-from: 1.2.0 + upgrade-from: 2.0.0 upgrade-from-extra-args: >- --set proxy.secretToken=aaaa1111 --set hub.cookieSecret=bbbb2222 From 76b22adaaed706d6613e019dcc3da4b6082cfa8d Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:34:27 +0000 Subject: [PATCH 801/898] Update jupyterhub/configurable-http-proxy version from 4.5.5 to 4.5.6 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 364af89847..1a20a3df5f 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -205,7 +205,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.5" # https://github.com/jupyterhub/configurable-http-proxy/tags + tag: "4.5.6" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From ae30a841e5a07e93fc864b6116f001c68c0fb4cc Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 10 Aug 2023 18:34:46 +0000 Subject: [PATCH 802/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 9d20e31651..d6c478106b 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -12,7 +12,7 @@ alembic==1.11.2 # via jupyterhub async-generator==1.10 # via jupyterhub -async-timeout==4.0.2 +async-timeout==4.0.3 # via aiohttp attrs==23.1.0 # via From c050b29bedd25171c639c4f9d1e518cfb538b01e Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 11 Aug 2023 11:43:35 +0000 Subject: [PATCH 803/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index d6c478106b..4bdba27a67 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -109,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.3 +oauthenticator==16.0.4 # via -r requirements.in oauthlib==3.2.2 # via From 8bcdcaf2bfb0182c21f2c33b78b679e0470bf734 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Aug 2023 14:10:33 +0200 Subject: [PATCH 804/898] Update changelog for 3.0.0 --- docs/source/changelog.md | 52 ++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 7c05ea0f5f..53d0622f4d 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,37 +14,33 @@ changes in pull requests], this list should be updated. ## 3.0 -### 3.0.0-beta.3 - 2023-07-06 +### 3.0.0 - 2023-08-11 -This is a beta release for testing before the 3.0.0 release. +This release updates JupyterHub itself and several dependencies to a new major +version, please read the breaking changes below before upgrading. -```{warning} -Since 3.0.0-beta.1 released 2023-06-12, another breaking change was made by -upgrading OAuthenticator from version 15.1.0 to 16.0.0. Please read to the -[OAuthenticator changelog]'s breaking changes before upgrading from -3.0.0-beta.1. - -Since 3.0.0-beta.3 released 2023-07-06, default networking rules related to -establishing connections to DNS ports has changed slightly as documented in the -breaking changes below. +```{warning} Breaking changes since beta releases +Since 3.0.0-beta.1 OAuthenticator was upgraded, and since 3.0.0-beta.3 default +networking rules related to establishing connections to DNS ports changed +slightly. ``` #### Breaking changes - K8s 1.23 is now required. - The Helm chart's provided images now use Python 3.11 instead of Python 3.9. -- JupyterHub 3.0.0 is upgraded to 4.0.1. +- JupyterHub 3.0.0 is upgraded to 4.0.2. - Please refer to the [JupyterHub changelog] for details, but note that this - upgrade won't require user servers to be restarted or first install version - 4 of `jupyterhub` (PyPI) or `jupyterhub-base` (conda-forge) in their user - environments. + upgrade doesn't require user servers to be restarted or that the user + environments have version 4 of `jupyterhub` (PyPI) or `jupyterhub-base` + (conda-forge). - KubeSpawner 4.2.0 is upgraded to 6.0.0 - Please read to the [KubeSpawner changelog]'s breaking changes and be aware that configuring [`singleuser.extraEnv`](schema_singleuser.extraEnv) is to configure `KubeSpawner.environment`, and to configure [`singleuser.profileList`](schema_singleuser.profileList) is to configure `KubeSpawner.profile_list`. -- OAuthenticator 15.1.0 is upgraded to 16.0.3. +- OAuthenticator 15.1.0 is upgraded to 16.0.4. - If you are using a JupyterHub Authenticator class from this project, please read to the [OAuthenticator changelog]'s breaking changes before upgrading this Helm chart. @@ -67,17 +63,17 @@ breaking changes below. | Dependency | Version in 2.0.0 | Version in 3.0.0 | Changelog link | Note | | -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------------------- | -| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.1 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [jupyterhub](https://github.com/jupyterhub/jupyterhub) | 3.0.0 | 4.0.2 | [Changelog](https://jupyterhub.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [kubespawner](https://github.com/jupyterhub/kubespawner) | 4.2.0 | 6.0.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | -| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.3 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 15.1.0 | 16.0.4 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | | [ldapauthenticator](https://github.com/jupyterhub/ldapauthenticator) | 1.3.2 | 1.3.2 | [Changelog](https://github.com/jupyterhub/ldapauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [ltiauthenticator](https://github.com/jupyterhub/ltiauthenticator) | 1.2.0 | 1.6.1 | [Changelog](https://github.com/jupyterhub/ltiauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [nativeauthenticator](https://github.com/jupyterhub/nativeauthenticator) | 1.1.0 | 1.2.0 | [Changelog](https://github.com/jupyterhub/nativeauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [tmpauthenticator](https://github.com/jupyterhub/tmpauthenticator) | 0.6 | 1.0.0 | [Changelog](https://github.com/jupyterhub/tmpauthenticator/blob/HEAD/CHANGELOG.md) | Run in the `hub` pod | | [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) | 1.2.1 | 1.2.1 | [Changelog](https://github.com/jupyterhub/jupyterhub-idle-culler/blob/main/CHANGELOG.md) | Run in the `hub` pod | -| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.3 | 4.5.5 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | -| [traefik](https://github.com/traefik/traefik) | v2.8.4 | v2.10.3 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | -| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.23.10 | v1.26.6 | [Changelog](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG) | Run in the `user-scheduler` pod(s) | +| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.3 | 4.5.6 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | +| [traefik](https://github.com/traefik/traefik) | v2.8.4 | v2.10.4 | [Changelog](https://github.com/traefik/traefik/blob/HEAD/CHANGELOG.md) | Run in the `autohttps` pod | +| [kube-scheduler](https://github.com/kubernetes/kube-scheduler) | v1.23.10 | v1.26.7 | [Changelog](https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG) | Run in the `user-scheduler` pod(s) | For a detailed list of Python dependencies in the `hub` Pod's Docker image, inspect the [images/hub/requirements.txt] file and use its git history to see @@ -87,14 +83,20 @@ what changes between tagged versions. #### New features added +- Add and enable two egressAllowRules to ensure DNS access [#3179](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3179) ([@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda), [@vizeit](https://github.com/vizeit)) - Add a jupyterhub/k8s-hub-slim image alongside jupyterhub/k8s-hub [#2920](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/2920) ([@consideRatio](https://github.com/consideRatio)) +#### Enhancements made + +- Allow `enabled` config, for use by charts depending on this chart conditionally [#3162](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3162) ([@monoakg](https://github.com/monoakg), [@consideRatio](https://github.com/consideRatio), [@manics](https://github.com/manics)) + #### Bugs fixed - Fix bugs related to installing chart multiple times in the same namespace [#3032](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3032) ([@HoseonRyu](https://github.com/HoseonRyu)) #### Maintenance and upkeep improvements +- maint: restrict allowed config with blockWithIpTables, add misc docs [#3192](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3192) ([@consideRatio](https://github.com/consideRatio), [@minrk](https://github.com/minrk)) - Update kubespawner 5.0.0 to 6.0.0, tmpauthenticator 0.6 to 1.0.0, nativeauthenticator 1.2.0 to 1.2.1, ltiauthenticator 1.5.0 to 1.5.1 [#3129](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3129) ([@jupyterhub-bot](https://github.com/jupyterhub-bot)) - Update kube-scheduler in user-scheduler from 1.25.9 to 1.26.4 [#3114](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3114) ([@consideRatio](https://github.com/consideRatio)) - Bump to kubespawner 5.0.0 and tornado 6.3 [#3095](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3095) ([@jupyterhub-bot](https://github.com/jupyterhub-bot)) @@ -116,6 +118,7 @@ what changes between tagged versions. #### Documentation improvements +- Add deprecation warning for `kube-lego` (https certificates) [#3186](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3186) ([@Ph0tonic](https://github.com/Ph0tonic), [@consideRatio](https://github.com/consideRatio)) - docs: let auth docs link to authenticator specific docs [#3151](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3151) ([@consideRatio](https://github.com/consideRatio)) - Enhance keycloak configuration example [#3142](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3142) ([@LucasVanHaaren](https://github.com/LucasVanHaaren)) - Show default value in configuration reference [#3138](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3138) ([@manics](https://github.com/manics)) @@ -158,9 +161,12 @@ what changes between tagged versions. #### Contributors to this release -([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-07-05&type=c)) +The following people contributed discussions, new ideas, code and documentation contributions, and review. +See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports). + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2022-09-09&to=2023-08-11&type=c)) -[@aaronjnewman](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-07-05&type=Issues) | [@alekseyolg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-07-05&type=Issues) | [@arunppsg](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-07-05&type=Issues) | [@betatim](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-07-05&type=Issues) | [@cbowman0](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-07-05&type=Issues) | [@ChristofKaufmann](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-07-05&type=Issues) | [@consideRatio](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-07-05&type=Issues) | [@dasantonym](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-07-05&type=Issues) | [@DeepSkyWonder](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-07-05&type=Issues) | [@ebebpl](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-07-05&type=Issues) | [@HoseonRyu](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-07-05&type=Issues) | [@IceS2](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-07-05&type=Issues) | [@JunaidChaudry](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-07-05&type=Issues) | [@kanor1306](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-07-05&type=Issues) | [@LucasVanHaaren](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ALucasVanHaaren+updated%3A2022-09-09..2023-07-05&type=Issues) | [@manics](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-07-05&type=Issues) | [@minrk](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-07-05&type=Issues) | [@pnasrat](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-07-05&type=Issues) | [@Uular](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-07-05&type=Issues) | [@xcompass](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-07-05&type=Issues) | [@yuvipanda](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-07-05&type=Issues) +@aaronjnewman ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aaaronjnewman+updated%3A2022-09-09..2023-08-11&type=Issues)) | @alekseyolg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aalekseyolg+updated%3A2022-09-09..2023-08-11&type=Issues)) | @arunppsg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aarunppsg+updated%3A2022-09-09..2023-08-11&type=Issues)) | @betatim ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abetatim+updated%3A2022-09-09..2023-08-11&type=Issues)) | @bjornjorgensen ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Abjornjorgensen+updated%3A2022-09-09..2023-08-11&type=Issues)) | @cbowman0 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acbowman0+updated%3A2022-09-09..2023-08-11&type=Issues)) | @choldgraf ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Acholdgraf+updated%3A2022-09-09..2023-08-11&type=Issues)) | @ChristofKaufmann ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AChristofKaufmann+updated%3A2022-09-09..2023-08-11&type=Issues)) | @consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2022-09-09..2023-08-11&type=Issues)) | @dasantonym ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Adasantonym+updated%3A2022-09-09..2023-08-11&type=Issues)) | @DeepSkyWonder ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ADeepSkyWonder+updated%3A2022-09-09..2023-08-11&type=Issues)) | @ebebpl ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aebebpl+updated%3A2022-09-09..2023-08-11&type=Issues)) | @HoseonRyu ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AHoseonRyu+updated%3A2022-09-09..2023-08-11&type=Issues)) | @iandesj ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aiandesj+updated%3A2022-09-09..2023-08-11&type=Issues)) | @IceS2 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AIceS2+updated%3A2022-09-09..2023-08-11&type=Issues)) | @JunaidChaudry ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AJunaidChaudry+updated%3A2022-09-09..2023-08-11&type=Issues)) | @jupyterhub-bot ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2022-09-09..2023-08-11&type=Issues)) | @kanor1306 ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Akanor1306+updated%3A2022-09-09..2023-08-11&type=Issues)) | @LucasVanHaaren ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3ALucasVanHaaren+updated%3A2022-09-09..2023-08-11&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2022-09-09..2023-08-11&type=Issues)) | @mathbunnyru ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amathbunnyru+updated%3A2022-09-09..2023-08-11&type=Issues)) | @mdlincoln ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amdlincoln+updated%3A2022-09-09..2023-08-11&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2022-09-09..2023-08-11&type=Issues)) | @monoakg ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amonoakg+updated%3A2022-09-09..2023-08-11&type=Issues)) | @Ph0tonic ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3APh0tonic+updated%3A2022-09-09..2023-08-11&type=Issues)) | @pnasrat ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Apnasrat+updated%3A2022-09-09..2023-08-11&type=Issues)) | @Uular ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AUular+updated%3A2022-09-09..2023-08-11&type=Issues)) | @vizeit ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Avizeit+updated%3A2022-09-09..2023-08-11&type=Issues)) | @xcompass ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Axcompass+updated%3A2022-09-09..2023-08-11&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2022-09-09..2023-08-11&type=Issues)) ## 2.0 From fd473532962f2b3db1f24a1b00709745800790d4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Aug 2023 14:43:20 +0200 Subject: [PATCH 805/898] Bump to 3.0.0 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index add277c6cd..fea8232124 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0-beta.3" + baseVersion: "3.0.0" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 21fc02759b..94b5def1b1 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0-beta.3" +current = "3.0.0" # match our prerelease prefixes # -alpha.1 From bfb05cd6cb7ad4488f32ac98adb9c5cd7d2d90b6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 11 Aug 2023 14:44:39 +0200 Subject: [PATCH 806/898] Bump to 3.0.1-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index fea8232124..1a25d5fbd5 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.0" + baseVersion: "3.0.1-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 94b5def1b1..fe15869d25 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.0" +current = "3.0.1-0.dev" # match our prerelease prefixes # -alpha.1 From 3be1ef9294fa71d2a8b86e1f46d05ee11bdc478f Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 15 Aug 2023 12:00:54 +0000 Subject: [PATCH 807/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 4bdba27a67..8c88894a69 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -109,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.4 +oauthenticator==16.0.5 # via -r requirements.in oauthlib==3.2.2 # via @@ -194,7 +194,7 @@ statsd==4.0.1 # via -r requirements.in text-unidecode==1.3 # via python-slugify -tornado==6.3.2 +tornado==6.3.3 # via # jupyterhub # jupyterhub-idle-culler diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 5ae7ca3a88..e2ac18f381 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -119,7 +119,7 @@ jupyter-events==0.7.0 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab -jupyter-server==2.7.0 +jupyter-server==2.7.1 # via # jupyter-lsp # jupyterlab @@ -133,7 +133,7 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==4.0.2 # via -r requirements.in -jupyterlab==4.0.4 +jupyterlab==4.0.5 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert @@ -292,7 +292,7 @@ terminado==0.17.1 # nbclassic tinycss2==1.2.1 # via nbconvert -tornado==6.3.2 +tornado==6.3.3 # via # ipykernel # jupyter-client From c70f634c7657ceef5afa60d812456e6c04dc4c7d Mon Sep 17 00:00:00 2001 From: Wermeille Bastien Date: Tue, 15 Aug 2023 14:19:28 +0200 Subject: [PATCH 808/898] Update services.md --- docs/source/administrator/services.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index 989328cffa..c53ada3977 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -68,4 +68,17 @@ hub: - port: 8181 targetPort: 8181 name: fastapi + +# Required if service should be publicly accessible +proxy: + chp: + networkPolicy: + egress: + - to: + - podSelector: + matchLabels: + app: jupyterhub + component: hub + ports: + - port: 8181 ``` From b4f331c34cc42c182d1accd195087dc6a51541ca Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 15 Aug 2023 14:51:13 +0200 Subject: [PATCH 809/898] Apply suggestions from code review --- docs/source/administrator/services.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index c53ada3977..7fe8c64c8f 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -69,7 +69,11 @@ hub: targetPort: 8181 name: fastapi -# Required if service should be publicly accessible +# The proxy.chp.networkPolicy.egress configuration below is required if the +# service should be accessible for users. If it shouldn't be, you should instead +# set the chart configuration services.fastapi.display to false as otherwise +# JupyterHub will provide a broken link in the Services menu for users to go to +# /services/fastapi/. proxy: chp: networkPolicy: From 389623683cde1f7075c2f1f72d453d1620a392f1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 15 Aug 2023 14:55:18 +0200 Subject: [PATCH 810/898] Add changelog for 3.0.1 --- docs/source/changelog.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 53d0622f4d..e2f0c27c79 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,6 +14,25 @@ changes in pull requests], this list should be updated. ## 3.0 +### 3.0.1 - 2023-08-15 + +#### Bugs fixed + +- Update oauthenticator from 16.0.4 to 16.0.5 and tornado from 6.3.2 to 6.3.3 [#3199](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3199) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) + +#### Documentation improvements + +- docs: fix the jupyterhub managed service example's networking rules [#3200](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3200) ([@Ph0tonic](https://github.com/Ph0tonic), [@consideRatio](https://github.com/consideRatio)) + +#### Contributors to this release + +The following people contributed discussions, new ideas, code and documentation contributions, and review. +See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports). + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2023-08-11&to=2023-08-15&type=c)) + +@consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2023-08-11..2023-08-15&type=Issues)) | @Ph0tonic ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3APh0tonic+updated%3A2023-08-11..2023-08-15&type=Issues)) + ### 3.0.0 - 2023-08-11 This release updates JupyterHub itself and several dependencies to a new major From e47cd01286f887d93c1f04fe3a192a9dda93df1a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 15 Aug 2023 15:00:07 +0200 Subject: [PATCH 811/898] docs: fix broken link about binary prefixes --- docs/source/jupyterhub/customizing/user-storage.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/source/jupyterhub/customizing/user-storage.md b/docs/source/jupyterhub/customizing/user-storage.md index 752a79d833..100faaf1c4 100644 --- a/docs/source/jupyterhub/customizing/user-storage.md +++ b/docs/source/jupyterhub/customizing/user-storage.md @@ -183,9 +183,12 @@ singleuser: This will request a `2Gi` volume per user. The default requests a `10Gi` volume per user. -We recommend you use the [IEC Prefixes](https://en.wikipedia.org/wiki/Binary_prefix#Adoption_by_IEC,_NIST_and_ISO) -(Ki, Mi, Gi, etc) for specifying how much storage you want. `2Gi` (IEC Prefix) is -`(2 * 1024 * 1024 * 1024)` bytes, while `2G` (SI Prefix) is `(2 * 1000 * 1000 * 1000)` bytes. +We recommend you use the [IEC binary prefixes] (Ki, Mi, Gi, etc) for specifying +how much storage you want. `2Gi` (IEC binary prefix) is `(2 * 1024 * 1024 * +1024)` bytes, while `2G` (SI decimal prefix) is `(2 * 1000 * 1000 * 1000)` +bytes. + +[iec binary prefixes]: https://en.wikipedia.org/wiki/Binary_prefix ## Turn off per-user persistent storage From 7707b012085cdf1e88f4fac4a63b85a3ac9f64b2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 15 Aug 2023 15:03:34 +0200 Subject: [PATCH 812/898] Bump to 3.0.1 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 1a25d5fbd5..27039b85ec 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.1-0.dev" + baseVersion: "3.0.1" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index fe15869d25..0359620e79 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.1-0.dev" +current = "3.0.1" # match our prerelease prefixes # -alpha.1 From 3f5cb91f5b306b631b5a256f57bcd67de2de85e3 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 15 Aug 2023 15:04:06 +0200 Subject: [PATCH 813/898] Bump to 3.0.2-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 27039b85ec..fafb7bba1e 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.1" + baseVersion: "3.0.2-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 0359620e79..1184300613 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.1" +current = "3.0.2-0.dev" # match our prerelease prefixes # -alpha.1 From 8316bb319d38b10ccb62a60d247a3008a2307e9a Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 17 Aug 2023 09:39:09 +0000 Subject: [PATCH 814/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 6 +++--- images/singleuser-sample/requirements.txt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 8c88894a69..e05766f65d 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.5 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.11.2 +alembic==1.11.3 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -109,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.5 +oauthenticator==16.0.6 # via -r requirements.in oauthlib==3.2.2 # via @@ -183,7 +183,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.19 +sqlalchemy==2.0.20 # via # alembic # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index e2ac18f381..dc847b24db 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,11 +4,11 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.11.2 +alembic==1.11.3 # via jupyterhub anyio==3.7.1 # via jupyter-server -argon2-cffi==21.3.0 +argon2-cffi==23.1.0 # via # jupyter-server # nbclassic @@ -156,7 +156,7 @@ nbclassic==1.0.0 # via -r requirements.in nbclient==0.8.0 # via nbconvert -nbconvert==7.7.3 +nbconvert==7.7.4 # via # jupyter-server # nbclassic @@ -279,7 +279,7 @@ sniffio==1.3.0 # via anyio soupsieve==2.4.1 # via beautifulsoup4 -sqlalchemy==2.0.19 +sqlalchemy==2.0.20 # via # alembic # jupyterhub From 0bc45799876702239821766dcc12cc3363e51590 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Aug 2023 12:19:31 +0200 Subject: [PATCH 815/898] Add changelog for 3.0.2 --- docs/source/changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index e2f0c27c79..a2e9484776 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,6 +14,16 @@ changes in pull requests], this list should be updated. ## 3.0 +### 3.0.2 - 2023-08-17 + +Includes a bugfix from the OAuthenticator project for users that have +`enable_auth_state` enabled with the Google, Globus, or BitBucket OAuthenticator +class. See the [oauthenticator changelog] for details. + +#### Bugs fixed + +- Update oauthenticator from 16.0.5 to 16.0.6 [#3203](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3203) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) + ### 3.0.1 - 2023-08-15 #### Bugs fixed From eb6cdb28ba0d74adef8f774c348a34c1742cd1d1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Aug 2023 12:33:22 +0200 Subject: [PATCH 816/898] docs: fix broken link --- docs/source/administrator/optimization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/administrator/optimization.md b/docs/source/administrator/optimization.md index d7857d8d05..c45f23d5dd 100644 --- a/docs/source/administrator/optimization.md +++ b/docs/source/administrator/optimization.md @@ -438,7 +438,7 @@ relevant: harder to debug. Various timeouts can be clues to suspect CPU starvation. 1. When scheduling a Pod on a node, the [_effective - requests/limits_](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#resources) + requests/limits_](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/#resource-sharing-within-containers) are considered. As a Pod's init containers are run in sequence before the Pod's main containers are started, the effective requests/limits are calculated as the highest of the init containers requests/limits and the sum From a9c456accb33b9db8f0b5a707602daf7e8a349a0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Aug 2023 12:33:43 +0200 Subject: [PATCH 817/898] docs: pin sphinx while waiting for a release of pydata-sphinx-theme --- docs/requirements.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index fa53c111a6..baede9d704 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,8 @@ +# FIXME: sphinx is pinned below 7.2 until pydata-sphinx-theme has a release +# newer than 0.13.3. Check for a new release at +# https://github.com/pydata/pydata-sphinx-theme/tags and remove the +# sphinx entry in this requirements file when its released. +sphinx<7.2 chartpress myst-parser pydata-sphinx-theme From 5c4549a3648b3ae1b7f596731c59a2da1a8b6d2d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Aug 2023 12:43:52 +0200 Subject: [PATCH 818/898] Bump to 3.0.2 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index fafb7bba1e..03aab3389d 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.2-0.dev" + baseVersion: "3.0.2" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 1184300613..d288e0a739 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.2-0.dev" +current = "3.0.2" # match our prerelease prefixes # -alpha.1 From 1c9a12cd94eed6842eee985d97126b8ed99e68ce Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 17 Aug 2023 12:44:13 +0200 Subject: [PATCH 819/898] Bump to 3.0.3-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 03aab3389d..d6c0296da7 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.2" + baseVersion: "3.0.3-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index d288e0a739..ad32dfa59f 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.2" +current = "3.0.3-0.dev" # match our prerelease prefixes # -alpha.1 From 83f8ffdde7c54328edf150c552fb2cfe6db0d1b3 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 21 Aug 2023 15:09:01 +0000 Subject: [PATCH 820/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- images/singleuser-sample/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e05766f65d..3a8e304249 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -109,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.6 +oauthenticator==16.0.7 # via -r requirements.in oauthlib==3.2.2 # via diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index dc847b24db..d72dd17f5b 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -119,7 +119,7 @@ jupyter-events==0.7.0 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab -jupyter-server==2.7.1 +jupyter-server==2.7.2 # via # jupyter-lsp # jupyterlab From a0ca73536805d9c31931af6b8b87f1b0408f4a62 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Mon, 21 Aug 2023 20:52:59 -0700 Subject: [PATCH 821/898] Add 2i2c as an institutional supporter I think there are now multiple people contributing significantly to z2jh from 2i2c, and I'd love for this callout to be here! I'd also love for other contributors to add their own institutions here :) --- docs/source/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/index.md b/docs/source/index.md index f5460ca5de..f316d889fb 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -138,6 +138,7 @@ the organizations that support them!) - [Berkeley Institute for Data Science](https://bids.berkeley.edu/) - [Cal Poly, San Luis Obispo](https://www.calpoly.edu/) - [Simula Research Institute](https://www.simula.no/) +- [2i2c](https://2i2c.org) ## Changelog From 33dd052914c4d3511e09fa711096e28dab5b5ff6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 22 Aug 2023 08:54:33 +0200 Subject: [PATCH 822/898] Add changelog for 3.0.3 --- docs/source/changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index a2e9484776..40cc9a90a8 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,6 +14,16 @@ changes in pull requests], this list should be updated. ## 3.0 +### 3.0.3 - 2023-08-22 + +Includes a bugfix from the OAuthenticator project for users of +GoogleOAuthenticator with `hosted_domain` and `admin_users` configured. See the +[oauthenticator changelog] for details. + +#### Bugs fixed + +- Update oauthenticator from 16.0.6 to 16.0.7 [#3207](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3207) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) + ### 3.0.2 - 2023-08-17 Includes a bugfix from the OAuthenticator project for users that have From db40a93d7f99981c061ad9593cb578574541f43a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 24 Aug 2023 05:08:27 +0000 Subject: [PATCH 823/898] Update kube-scheduler version from v1.26.7 to v1.26.8 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 1a20a3df5f..99b7ea4491 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -519,7 +519,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.7" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.8" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 1ebca266bed3e2f38332c5a9a3202f627cba3af0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 29 Aug 2023 08:59:45 +0200 Subject: [PATCH 824/898] Bump to 3.0.3 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index d6c0296da7..6ac55a245b 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.3-0.dev" + baseVersion: "3.0.3" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index ad32dfa59f..1ffb1bf314 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.3-0.dev" +current = "3.0.3" # match our prerelease prefixes # -alpha.1 From a27036dda40ff1d5e58da005c1d182da3cdaca60 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 29 Aug 2023 08:59:57 +0200 Subject: [PATCH 825/898] Bump to 3.0.4-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 6ac55a245b..034960c4bd 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.3" + baseVersion: "3.0.4-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 1ffb1bf314..951801ef6e 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.3" +current = "3.0.4-0.dev" # match our prerelease prefixes # -alpha.1 From 6b0574343cba6d472ec6910e9cf49d2896831592 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Tue, 29 Aug 2023 10:04:26 +0200 Subject: [PATCH 826/898] docs: fix changelog date entry for 3.0.3 --- docs/source/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 40cc9a90a8..a43495947d 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,7 +14,7 @@ changes in pull requests], this list should be updated. ## 3.0 -### 3.0.3 - 2023-08-22 +### 3.0.3 - 2023-08-29 Includes a bugfix from the OAuthenticator project for users of GoogleOAuthenticator with `hosted_domain` and `admin_users` configured. See the From bd20b8a5a8ab8c6dc039bf65948ab4aee17a31b7 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 4 Sep 2023 05:13:11 +0000 Subject: [PATCH 827/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index d8eeac2248..98d2426232 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-07-03_05:15:39 +# VULN_SCAN_TIME=2023-09-04_05:13:09 # The build stage From 6028eea1046ea24d2b1db5fffd40fc80a5a93fe5 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 4 Sep 2023 05:13:44 +0000 Subject: [PATCH 828/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 4a28e98f21..21faddd788 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-07-03_05:15:54 +# VULN_SCAN_TIME=2023-09-04_05:13:41 # The build stage From f33fab9ed7f9014b4a00c0df064aa70158691f8a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:11:44 +0000 Subject: [PATCH 829/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-prettier: v3.0.0 → v3.0.3](https://github.com/pre-commit/mirrors-prettier/compare/v3.0.0...v3.0.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8929796657..12802d5918 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -54,7 +54,7 @@ repos: # Autoformat: markdown, yaml (but not helm templates) - repo: https://github.com/pre-commit/mirrors-prettier - rev: v3.0.0 + rev: v3.0.3 hooks: - id: prettier From 1f16dd026d81678d29ff036b8fd5bd8d73539835 Mon Sep 17 00:00:00 2001 From: Andrii Salnikov Date: Mon, 11 Sep 2023 15:40:28 +0200 Subject: [PATCH 830/898] profile_options support for prePulling profile images --- .../image-puller/_helpers-daemonset.tpl | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl index 1fe8276e3e..610f8bde92 100644 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -134,6 +134,7 @@ spec: {{- /* --- Conditionally pull profileList images --- */}} {{- if .Values.prePuller.pullProfileListImages }} {{- range $k, $container := .Values.singleuser.profileList }} + {{- /* profile's kubespawner_override */}} {{- if $container.kubespawner_override }} {{- if $container.kubespawner_override.image }} - name: image-pull-singleuser-profilelist-{{ $k }} @@ -152,6 +153,33 @@ spec: {{- end }} {{- end }} {{- end }} + {{- /* kubespawner_override in profile's profile_options */}} + {{- if $container.profile_options }} + {{- range $option, $option_spec := $container.profile_options }} + {{- if $option_spec.choices }} + {{- range $choice, $choice_spec := $option_spec.choices }} + {{- if $choice_spec.kubespawner_override }} + {{- if $choice_spec.kubespawner_override.image }} + - name: image-pull-profile-{{ $k }}-option-{{ $option }}-{{ $choice }} + image: {{ $choice_spec.kubespawner_override.image }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with $.Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with $.Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{- end }} From 2b760661ee66350611e2190a466b9d237006776a Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 14 Sep 2023 05:08:40 +0000 Subject: [PATCH 831/898] Update kube-scheduler version from v1.26.8 to v1.26.9 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 99b7ea4491..082240af9d 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -519,7 +519,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.8" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.9" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From d750eda4fa966aa3fef94b6f53910fc10bd2398c Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Wed, 20 Sep 2023 05:08:48 +0000 Subject: [PATCH 832/898] Update jupyterhub/configurable-http-proxy version from 4.5.6 to 4.6.0 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 082240af9d..5b1ea8b6a6 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -205,7 +205,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.5.6" # https://github.com/jupyterhub/configurable-http-proxy/tags + tag: "4.6.0" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From 395815f335d9beb5b39c65a8bec48c0d6bea480e Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Fri, 22 Sep 2023 05:33:48 +0000 Subject: [PATCH 833/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 18 +++++----- images/singleuser-sample/requirements.txt | 42 +++++++++++------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3a8e304249..a623bc6df0 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -8,7 +8,7 @@ aiohttp==3.8.5 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.11.3 +alembic==1.12.0 # via jupyterhub async-generator==1.10 # via jupyterhub @@ -35,7 +35,7 @@ charset-normalizer==3.2.0 # via # aiohttp # requests -cryptography==41.0.3 +cryptography==41.0.4 # via # pyjwt # pyopenssl @@ -57,7 +57,7 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.19.0 +jsonschema==4.19.1 # via # jupyter-telemetry # oauthenticator @@ -91,7 +91,7 @@ jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in jupyterhub-tmpauthenticator==1.0.0 # via -r requirements.in -kubernetes-asyncio==24.2.3 +kubernetes-asyncio==25.11.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -167,7 +167,7 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -rpds-py==0.9.2 +rpds-py==0.10.3 # via # jsonschema # referencing @@ -183,7 +183,7 @@ six==1.16.0 # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.20 +sqlalchemy==2.0.21 # via # alembic # jupyterhub @@ -200,7 +200,7 @@ tornado==6.3.3 # jupyterhub-idle-culler # jupyterhub-ldapauthenticator # oauthenticator -traitlets==5.9.0 +traitlets==5.10.0 # via # jupyter-telemetry # jupyterhub @@ -208,11 +208,11 @@ traitlets==5.9.0 # jupyterhub-ldapauthenticator # jupyterhub-ltiauthenticator # oauthenticator -typing-extensions==4.7.1 +typing-extensions==4.8.0 # via # alembic # sqlalchemy -urllib3==2.0.4 +urllib3==2.0.5 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index d72dd17f5b..49f8c8c9be 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,9 +4,9 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.11.3 +alembic==1.12.0 # via jupyterhub -anyio==3.7.1 +anyio==4.0.0 # via jupyter-server argon2-cffi==23.1.0 # via @@ -16,7 +16,7 @@ argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.2.3 # via isoduration -asttokens==2.2.1 +asttokens==2.4.0 # via stack-data async-generator==1.10 # via jupyterhub @@ -46,9 +46,9 @@ charset-normalizer==3.2.0 # via requests comm==0.1.4 # via ipykernel -cryptography==41.0.3 +cryptography==41.0.4 # via pyopenssl -debugpy==1.6.7.post1 +debugpy==1.8.0 # via ipykernel decorator==5.1.1 # via ipython @@ -67,11 +67,11 @@ idna==3.4 # anyio # jsonschema # requests -ipykernel==6.25.1 +ipykernel==6.25.2 # via # jupyterlab # nbclassic -ipython==8.14.0 +ipython==8.15.0 # via ipykernel ipython-genutils==0.2.0 # via nbclassic @@ -91,7 +91,7 @@ json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.19.0 +jsonschema[format-nongpl]==4.19.1 # via # jupyter-events # jupyter-telemetry @@ -99,7 +99,7 @@ jsonschema[format-nongpl]==4.19.0 # nbformat jsonschema-specifications==2023.7.1 # via jsonschema -jupyter-client==8.3.0 +jupyter-client==8.3.1 # via # ipykernel # jupyter-server @@ -119,7 +119,7 @@ jupyter-events==0.7.0 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab -jupyter-server==2.7.2 +jupyter-server==2.7.3 # via # jupyter-lsp # jupyterlab @@ -133,11 +133,11 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==4.0.2 # via -r requirements.in -jupyterlab==4.0.5 +jupyterlab==4.0.6 # via -r requirements.in jupyterlab-pygments==0.2.2 # via nbconvert -jupyterlab-server==2.24.0 +jupyterlab-server==2.25.0 # via jupyterlab mako==1.2.4 # via alembic @@ -156,7 +156,7 @@ nbclassic==1.0.0 # via -r requirements.in nbclient==0.8.0 # via nbconvert -nbconvert==7.7.4 +nbconvert==7.8.0 # via # jupyter-server # nbclassic @@ -168,7 +168,7 @@ nbformat==5.9.2 # nbconvert nbgitpuller==1.2.0 # via -r requirements.in -nest-asyncio==1.5.7 +nest-asyncio==1.5.8 # via # ipykernel # nbclassic @@ -257,7 +257,7 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rpds-py==0.9.2 +rpds-py==0.10.3 # via # jsonschema # referencing @@ -277,9 +277,9 @@ six==1.16.0 # rfc3339-validator sniffio==1.3.0 # via anyio -soupsieve==2.4.1 +soupsieve==2.5 # via beautifulsoup4 -sqlalchemy==2.0.20 +sqlalchemy==2.0.21 # via # alembic # jupyterhub @@ -302,7 +302,7 @@ tornado==6.3.3 # nbclassic # nbgitpuller # terminado -traitlets==5.9.0 +traitlets==5.10.0 # via # comm # ipykernel @@ -319,13 +319,13 @@ traitlets==5.9.0 # nbclient # nbconvert # nbformat -typing-extensions==4.7.1 +typing-extensions==4.8.0 # via # alembic # sqlalchemy uri-template==1.3.0 # via jsonschema -urllib3==2.0.4 +urllib3==2.0.5 # via requests wcwidth==0.2.6 # via prompt-toolkit @@ -335,5 +335,5 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.6.1 +websocket-client==1.6.3 # via jupyter-server From 9256c929da7421339b0df70dfa0d78cd9fcb7834 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 05:13:36 +0000 Subject: [PATCH 834/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 98d2426232..0b6132e3bc 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-09-04_05:13:09 +# VULN_SCAN_TIME=2023-09-25_05:13:34 # The build stage From e627e9c48659a98c4500c1bb8b7cc6568509ab2d Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 05:13:55 +0000 Subject: [PATCH 835/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index 21faddd788..a3ac192b04 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-09-04_05:13:41 +# VULN_SCAN_TIME=2023-09-25_05:13:53 # The build stage From e1c0f193365a45603ae251cb67b9b6c1f363cf86 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 26 Sep 2023 06:51:22 +0000 Subject: [PATCH 836/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index a623bc6df0..716b872524 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -91,7 +91,7 @@ jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in jupyterhub-tmpauthenticator==1.0.0 # via -r requirements.in -kubernetes-asyncio==25.11.0 +kubernetes-asyncio==26.9.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator From 9cbf0c6351a85119e38a266696b165297409c3fe Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 28 Sep 2023 17:08:09 +0000 Subject: [PATCH 837/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 10 +++++----- images/singleuser-sample/requirements.txt | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 716b872524..00bbbac466 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -81,7 +81,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==6.0.0 +jupyterhub-kubespawner==6.1.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -109,7 +109,7 @@ mwoauth==0.3.8 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.0.7 +oauthenticator==16.1.0 # via -r requirements.in oauthlib==3.2.2 # via @@ -125,7 +125,7 @@ pamela==1.1.0 # via jupyterhub prometheus-client==0.17.1 # via jupyterhub -psycopg2==2.9.7 +psycopg2==2.9.8 # via -r requirements.in pyasn1==0.5.0 # via ldap3 @@ -171,7 +171,7 @@ rpds-py==0.10.3 # via # jsonschema # referencing -ruamel-yaml==0.17.32 +ruamel-yaml==0.17.33 # via # jupyter-telemetry # oauthenticator @@ -200,7 +200,7 @@ tornado==6.3.3 # jupyterhub-idle-culler # jupyterhub-ldapauthenticator # oauthenticator -traitlets==5.10.0 +traitlets==5.10.1 # via # jupyter-telemetry # jupyterhub diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 49f8c8c9be..2c84313e2f 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -105,7 +105,7 @@ jupyter-client==8.3.1 # jupyter-server # nbclassic # nbclient -jupyter-core==5.3.1 +jupyter-core==5.3.2 # via # ipykernel # jupyter-client @@ -261,7 +261,7 @@ rpds-py==0.10.3 # via # jsonschema # referencing -ruamel-yaml==0.17.32 +ruamel-yaml==0.17.33 # via jupyter-telemetry ruamel-yaml-clib==0.2.7 # via ruamel-yaml @@ -302,7 +302,7 @@ tornado==6.3.3 # nbclassic # nbgitpuller # terminado -traitlets==5.10.0 +traitlets==5.10.1 # via # comm # ipykernel From f99802caea26678306d1ffc6c0cbb9333d0fa565 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 29 Sep 2023 20:00:02 +0200 Subject: [PATCH 838/898] Add changelog for 3.1.0 --- docs/source/changelog.md | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index a43495947d..69f27d7131 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,6 +12,49 @@ changes in pull requests], this list should be updated. [development releases]: https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub [breaking changes in pull requests]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking +## 3.1 + +### 3.1.0 - 2023-09-29 + +```{warning} Post-upgrade action recommended +A bug in KubeSpawner 5.0-6.0 present in z2jh 3.0.0-3.0.3 made user server pods +risk be orphaned by JupyterHub, making them run indefinitely and cause +unnecessary cloud costs. + +Read more about how to clean up these user server pods in [this forum post]. + +[this forum post]: https://discourse.jupyter.org/t/how-to-cleanup-orphaned-user-pods-after-bug-in-z2jh-3-0-and-kubespawner-6-0/21677 +``` + +#### Notable dependencies updated + +| Dependency | Version in 3.0.3 | Version in 3.1.0 | Changelog link | Note | +| -------------------------------------------------------------------------------- | ---------------- | ---------------- | ----------------------------------------------------------------------------------------- | ---------------------- | +| [kubespawner](https://github.com/jupyterhub/kubespawner) | 6.0.0 | 6.1.0 | [Changelog](https://jupyterhub-kubespawner.readthedocs.io/en/stable/changelog.html) | Run in the `hub` pod | +| [oauthenticator](https://github.com/jupyterhub/oauthenticator) | 16.0.7 | 16.1.0 | [Changelog](https://oauthenticator.readthedocs.io/en/stable/reference/changelog.html) | Run in the `hub` pod | +| [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) | 4.5.6 | 4.6.0 | [Changelog](https://github.com/jupyterhub/configurable-http-proxy/blob/HEAD/CHANGELOG.md) | Run in the `proxy` pod | + +#### Dependency updates + +- Update jupyterhub/configurable-http-proxy version from 4.5.6 to 4.6.0 [#3224](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3224) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@manics](https://github.com/manics)) +- Update kube-scheduler version from v1.26.8 to v1.26.9 [#3220](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3220) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@manics](https://github.com/manics)) +- Update oauthenticator from 16.0.7 to 16.1.0, and kubespawner from 6.0.0 to 6.1.0 [#3234](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3234) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) +- Update kubernetes_asyncio from 25.11.0 to 26.9.0 [#3233](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3233) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) +- Update kubernetes_asyncio from 24.2.3 to 25.11.0 [#3228](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3228) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) + +#### Documentation improvements + +- docs: fix changelog date entry for 3.0.3 [#3211](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3211) ([@consideRatio](https://github.com/consideRatio)) + +#### Contributors to this release + +The following people contributed discussions, new ideas, code and documentation contributions, and review. +See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports). + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2023-08-29&to=2023-09-29&type=c)) + +@consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2023-08-29..2023-09-29&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2023-08-29..2023-09-29&type=Issues)) | @shaneknapp ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ashaneknapp+updated%3A2023-08-29..2023-09-29&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2023-08-29..2023-09-29&type=Issues)) + ## 3.0 ### 3.0.3 - 2023-08-29 From 8ccf2609ba5a42a9430f6940c04b2d8fab9bfc1d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 29 Sep 2023 21:46:58 +0200 Subject: [PATCH 839/898] Bump to 3.1.0 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 034960c4bd..8609d8d871 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.0.4-0.dev" + baseVersion: "3.1.0" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 951801ef6e..d1500bc8f4 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.0.4-0.dev" +current = "3.1.0" # match our prerelease prefixes # -alpha.1 From de94851e248377c51633453ddeb682d4ab3a7144 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Fri, 29 Sep 2023 21:48:12 +0200 Subject: [PATCH 840/898] Bump to 3.1.1-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 8609d8d871..3eaf6531f3 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.1.0" + baseVersion: "3.1.1-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index d1500bc8f4..c3babaaddd 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.1.0" +current = "3.1.1-0.dev" # match our prerelease prefixes # -alpha.1 From 993253136096f0015615a7180542da26c04653d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 05:04:52 +0000 Subject: [PATCH 841/898] build(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-chart.yaml | 8 ++++---- .github/workflows/test-docker-build.yaml | 2 +- .github/workflows/test-docs.yaml | 2 +- .github/workflows/vuln-scan.yaml | 2 +- .github/workflows/watch-dependencies.yaml | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c746653d0f..920328a813 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,7 +36,7 @@ jobs: publish: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 90e0593c4c..1e3d23fb40 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -30,7 +30,7 @@ jobs: lint_shell_scripts: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.11" @@ -44,7 +44,7 @@ jobs: lint_and_validate_rendered_templates: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.11" @@ -77,7 +77,7 @@ jobs: - helm-version: v3.5.0 # minimal required version steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.11" @@ -213,7 +213,7 @@ jobs: --set audit.clientMinMessages=debug steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index e91549c9b6..702c31868b 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -31,7 +31,7 @@ jobs: build_images: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml index b624936259..4986b5d065 100644 --- a/.github/workflows/test-docs.yaml +++ b/.github/workflows/test-docs.yaml @@ -27,7 +27,7 @@ jobs: linkcheck: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # chartpress, used by docs/conf.py, requires git history to set # chart version and image tags correctly diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 4e0232eba2..77aa6eacde 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -51,7 +51,7 @@ jobs: accept_failure: false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: # chartpress requires git history to set chart version and image tags # correctly diff --git a/.github/workflows/watch-dependencies.yaml b/.github/workflows/watch-dependencies.yaml index efd2f88d47..635acc360c 100644 --- a/.github/workflows/watch-dependencies.yaml +++ b/.github/workflows/watch-dependencies.yaml @@ -79,7 +79,7 @@ jobs: version_patch_regexp_group_suffix: "?" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get values.yaml pinned tag of ${{ matrix.registry }}/${{ matrix.repository }} id: local @@ -134,7 +134,7 @@ jobs: environment: watch-dependencies steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-python@v4 with: python-version: "3.11" @@ -206,7 +206,7 @@ jobs: environment: watch-dependencies steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Refreeze images/*/requirements.txt based on images/*/requirements.in run: ci/refreeze From 54d8751104e51012e450676105c54b4016a07d87 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 05:04:55 +0000 Subject: [PATCH 842/898] build(deps): bump docker/setup-buildx-action from 2 to 3 Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-buildx-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c746653d0f..3f0bc99f53 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -72,7 +72,7 @@ jobs: uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Install chart publishing dependencies (chartpress, helm) run: | diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index e91549c9b6..7b3f42921d 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -48,7 +48,7 @@ jobs: uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx (for chartpress multi-arch builds) - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Build a multiple architecture Docker image run: >- From 9ef4f708efcbc0f1c6659ad41230757ab7f51c32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 05:05:01 +0000 Subject: [PATCH 843/898] build(deps): bump aquasecurity/trivy-action from 0.11.2 to 0.12.0 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.11.2 to 0.12.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/41f05d9ecffa2ed3f1580af306000f734b733e54...fbd16365eb88e12433951383f5e99bd901fc618f) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 4e0232eba2..91b10cdd3a 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 + uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 + uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@41f05d9ecffa2ed3f1580af306000f734b733e54 + uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f with: image-ref: rebuilt-image format: table From 3585d57963ce9e2813d3b3f70c49449b76980fa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 08:25:29 +0000 Subject: [PATCH 844/898] build(deps): bump docker/setup-qemu-action from 2 to 3 Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 2 +- .github/workflows/test-docker-build.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d29aa4be65..5a743f863c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -69,7 +69,7 @@ jobs: f.write(f"publishing={publishing}\n") - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@v3 diff --git a/.github/workflows/test-docker-build.yaml b/.github/workflows/test-docker-build.yaml index f12e1995c5..366dfdfe19 100644 --- a/.github/workflows/test-docker-build.yaml +++ b/.github/workflows/test-docker-build.yaml @@ -45,7 +45,7 @@ jobs: run: pip install chartpress - name: Set up QEMU (for docker buildx) - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx (for chartpress multi-arch builds) uses: docker/setup-buildx-action@v3 From 8210880f789b490c7839ea03129536c2a4378697 Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Tue, 3 Oct 2023 08:06:04 +0000 Subject: [PATCH 845/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 12 ++++----- images/singleuser-sample/requirements.txt | 32 ++++++++++++----------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 00bbbac466..91a7bff7f7 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -29,9 +29,9 @@ certifi==2023.7.22 # requests certipy==0.1.3 # via jupyterhub -cffi==1.15.1 +cffi==1.16.0 # via cryptography -charset-normalizer==3.2.0 +charset-normalizer==3.3.0 # via # aiohttp # requests @@ -47,7 +47,7 @@ frozenlist==1.4.0 # via # aiohttp # aiosignal -greenlet==2.0.2 +greenlet==3.0.0 # via sqlalchemy idna==3.4 # via @@ -91,7 +91,7 @@ jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in jupyterhub-tmpauthenticator==1.0.0 # via -r requirements.in -kubernetes-asyncio==26.9.0 +kubernetes-asyncio==27.6.0 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator @@ -119,7 +119,7 @@ oauthlib==3.2.2 # requests-oauthlib onetimepass==1.0.1 # via jupyterhub-nativeauthenticator -packaging==23.1 +packaging==23.2 # via jupyterhub pamela==1.1.0 # via jupyterhub @@ -212,7 +212,7 @@ typing-extensions==4.8.0 # via # alembic # sqlalchemy -urllib3==2.0.5 +urllib3==2.0.6 # via # jupyterhub-kubespawner # kubernetes-asyncio diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 2c84313e2f..e7cde70efa 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -14,7 +14,7 @@ argon2-cffi==23.1.0 # nbclassic argon2-cffi-bindings==21.2.0 # via argon2-cffi -arrow==1.2.3 +arrow==1.3.0 # via isoduration asttokens==2.4.0 # via stack-data @@ -26,7 +26,7 @@ attrs==23.1.0 # via # jsonschema # referencing -babel==2.12.1 +babel==2.13.0 # via jupyterlab-server backcall==0.2.0 # via ipython @@ -38,11 +38,11 @@ certifi==2023.7.22 # via requests certipy==0.1.3 # via jupyterhub -cffi==1.15.1 +cffi==1.16.0 # via # argon2-cffi-bindings # cryptography -charset-normalizer==3.2.0 +charset-normalizer==3.3.0 # via requests comm==0.1.4 # via ipykernel @@ -54,13 +54,13 @@ decorator==5.1.1 # via ipython defusedxml==0.7.1 # via nbconvert -executing==1.2.0 +executing==2.0.0 # via stack-data -fastjsonschema==2.18.0 +fastjsonschema==2.18.1 # via nbformat fqdn==1.5.1 # via jsonschema -greenlet==2.0.2 +greenlet==3.0.0 # via sqlalchemy idna==3.4 # via @@ -71,13 +71,13 @@ ipykernel==6.25.2 # via # jupyterlab # nbclassic -ipython==8.15.0 +ipython==8.16.1 # via ipykernel ipython-genutils==0.2.0 # via nbclassic isoduration==20.11.0 # via jsonschema -jedi==0.19.0 +jedi==0.19.1 # via ipython jinja2==3.1.2 # via @@ -150,7 +150,7 @@ matplotlib-inline==0.1.6 # via # ipykernel # ipython -mistune==3.0.1 +mistune==3.0.2 # via nbconvert nbclassic==1.0.0 # via -r requirements.in @@ -180,7 +180,7 @@ oauthlib==3.2.2 # via jupyterhub overrides==7.4.0 # via jupyter-server -packaging==23.1 +packaging==23.2 # via # ipykernel # jupyter-server @@ -198,7 +198,7 @@ pexpect==4.8.0 # via ipython pickleshare==0.7.5 # via ipython -platformdirs==3.10.0 +platformdirs==3.11.0 # via jupyter-core prometheus-client==0.17.1 # via @@ -283,7 +283,7 @@ sqlalchemy==2.0.21 # via # alembic # jupyterhub -stack-data==0.6.2 +stack-data==0.6.3 # via ipython terminado==0.17.1 # via @@ -319,15 +319,17 @@ traitlets==5.10.1 # nbclient # nbconvert # nbformat +types-python-dateutil==2.8.19.14 + # via arrow typing-extensions==4.8.0 # via # alembic # sqlalchemy uri-template==1.3.0 # via jsonschema -urllib3==2.0.5 +urllib3==2.0.6 # via requests -wcwidth==0.2.6 +wcwidth==0.2.8 # via prompt-toolkit webcolors==1.13 # via jsonschema From d055eab4038a11143f9b47572436dc4c7cc6846b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 10:36:25 +0000 Subject: [PATCH 846/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.10.1 → v3.14.0](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.14.0) - [github.com/psf/black: 23.7.0 → 23.9.1](https://github.com/psf/black/compare/23.7.0...23.9.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 12802d5918..0a443bd99a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.14.0 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black args: From c7b7baa270d179b030859636ac29837b657753e9 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 12 Oct 2023 05:09:15 +0000 Subject: [PATCH 847/898] Update library/traefik version from v2.10.4 to v2.10.5 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5b1ea8b6a6..740b53e521 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -258,7 +258,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "v2.10.4" # ref: https://hub.docker.com/_/traefik?tab=tags + tag: "v2.10.5" # ref: https://hub.docker.com/_/traefik?tab=tags pullPolicy: pullSecrets: [] hsts: From 914b57914fa6414f4342b51f5ef526c442647bcc Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 05:13:05 +0000 Subject: [PATCH 848/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 1093630acd..63590a9267 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-07-31_05:12:49 +# VULN_SCAN_TIME=2023-10-16_05:13:02 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From 602a1c654c7be38c38d4d43a56db7219b72320e5 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 05:14:01 +0000 Subject: [PATCH 849/898] Patch known vulnerability in hub --- images/hub/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 0b6132e3bc..83f5871e5d 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-09-25_05:13:34 +# VULN_SCAN_TIME=2023-10-16_05:13:59 # The build stage From 7e28cf297429f1f441627900f3ad48daf3b9bdfb Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 16 Oct 2023 05:14:02 +0000 Subject: [PATCH 850/898] Patch known vulnerability in singleuser-sample --- images/singleuser-sample/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index a3ac192b04..dc4da26bc1 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -1,5 +1,5 @@ # syntax = docker/dockerfile:1.3 -# VULN_SCAN_TIME=2023-09-25_05:13:53 +# VULN_SCAN_TIME=2023-10-16_05:14:00 # The build stage From 1f760340c3ec8803518053c0af0007e053576e94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 21:04:40 +0000 Subject: [PATCH 851/898] build(deps): bump urllib3 from 2.0.6 to 2.0.7 in /images/hub Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.0.6...2.0.7) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 91a7bff7f7..3bc179f110 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -212,7 +212,7 @@ typing-extensions==4.8.0 # via # alembic # sqlalchemy -urllib3==2.0.6 +urllib3==2.0.7 # via # jupyterhub-kubespawner # kubernetes-asyncio From bec21a428e6c8e406ff39cdd25ca8a9d06e46fa2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 21:06:16 +0000 Subject: [PATCH 852/898] build(deps): bump urllib3 in /images/singleuser-sample Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.0.6 to 2.0.7. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.0.6...2.0.7) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/singleuser-sample/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index e7cde70efa..b828ae9bca 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -327,7 +327,7 @@ typing-extensions==4.8.0 # sqlalchemy uri-template==1.3.0 # via jsonschema -urllib3==2.0.6 +urllib3==2.0.7 # via requests wcwidth==0.2.8 # via prompt-toolkit From c6e680e5b15338388e4c8bb9b25518b700c8c249 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 09:56:34 +0530 Subject: [PATCH 853/898] Use quay.io as source of docker images While we do have 'sponsored OSS' status on dockerhub, @choldgraf (whose credit card is apparently tied to the dockerhub jupyterhub account) just got a surprise multi hundred dollar bill for that organization! While I'm sure this is a mistake and can be corrected, it's IMO enough incentive for us to move away for real. --- .github/workflows/publish.yml | 2 +- chartpress.yaml | 2 +- jupyterhub/values.yaml | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5a743f863c..118731b240 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -108,7 +108,7 @@ jobs: # https://github.com/jupyterhub/zero-to-jupyterhub-k8s/settings/secrets/actions if: steps.publishing.outputs.publishing run: | - docker login -u "${{ secrets.DOCKER_USERNAME }}" -p "${{ secrets.DOCKER_PASSWORD }}" + docker login -u "${{ secrets.QUAY_USERNAME }}" -p "${{ secrets.QUAY_PASSWORD }}" quay.io - name: Configure a git user # Having a user.email and user.name configured with git is required to diff --git a/chartpress.yaml b/chartpress.yaml index 3eaf6531f3..a0ce33efe6 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -12,7 +12,7 @@ charts: - name: jupyterhub # Dev: imagePrefix can be useful to override if you want to trial something # locally developed in a remote k8s cluster. - imagePrefix: jupyterhub/k8s- + imagePrefix: quay.io/jupyterhub/k8s- # baseVersion should be a -0.dev suffixed version, where the version should # be the next major, minor, or patch version depending on what we have # merged so far into the main branch. If for example we have merged a diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 740b53e521..2f7eac35cc 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -83,7 +83,7 @@ hub: extraVolumes: [] extraVolumeMounts: [] image: - name: jupyterhub/k8s-hub + name: quay.io/jupyterhub/k8s-hub tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -201,7 +201,7 @@ proxy: runAsGroup: 65534 # nobody group allowPrivilegeEscalation: false image: - name: jupyterhub/configurable-http-proxy + name: quay.io/jupyterhub/configurable-http-proxy # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # @@ -304,7 +304,7 @@ proxy: runAsGroup: 65534 # nobody group allowPrivilegeEscalation: false image: - name: jupyterhub/k8s-secret-sync + name: quay.io/jupyterhub/k8s-secret-sync tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -396,7 +396,7 @@ singleuser: volumeNameTemplate: volume-{username}{servername} storageAccessModes: [ReadWriteOnce] image: - name: jupyterhub/k8s-singleuser-sample + name: quay.io/jupyterhub/k8s-singleuser-sample tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -605,7 +605,7 @@ prePuller: pullOnlyOnChanges: true # image and the configuration below relates to the hook-image-awaiter Job image: - name: jupyterhub/k8s-image-awaiter + name: quay.io/jupyterhub/k8s-image-awaiter tag: "set-by-chartpress" pullPolicy: pullSecrets: [] From e4199060449c7745660bf9a101ad7b5f3654ab7a Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 11:39:09 +0530 Subject: [PATCH 854/898] Test correct image name for k8s-hub-slim image --- .github/workflows/test-chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 1e3d23fb40..93362a98fc 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -134,7 +134,7 @@ jobs: - k3s-channel: stable # also test hub-slim, and prePuller.hook test: install local-chart-extra-args: >- - --set hub.image.name=jupyterhub/k8s-hub-slim + --set hub.image.name=quay.io/jupyterhub/k8s-hub-slim --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - k3s-channel: v1.26 # also test hub.existingSecret From 04a04466940b028174c2d0bcaddd29aad2319220 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 11:40:37 +0530 Subject: [PATCH 855/898] Update some documentation about where images live --- .github/workflows/publish.yml | 2 +- CONTRIBUTING.md | 2 +- RELEASE.md | 2 +- docs/source/administrator/security.md | 4 ++-- docs/source/administrator/services.md | 4 ++-- images/hub/Dockerfile | 2 +- images/singleuser-sample/README.md | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 118731b240..4d3f01cbc0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,7 +31,7 @@ jobs: # JupyterHub organization Helm chart repository. # # ref: https://github.com/jupyterhub/helm-chart - # ref: https://hub.docker.com/orgs/jupyterhub + # ref: https://quay.io/organization/jupyterhub publish: runs-on: ubuntu-22.04 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e607d4302d..eee8ebd800 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -331,7 +331,7 @@ Did you get an error like one of these below? # while running apt-get install while building a docker image with chartpress E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/r/rtmpdump/librtmp1_2.4+20151223.gitfa8646d.1-1_amd64.deb Could not connect to archive.ubuntu.com:80 (91.189.88.174). - connect (113: No route to host) Could not connect to archive.ubuntu.com:80 (91.189.88.31). - connect (113: No route to host) [IP: 91.189.88.174 80] # [...] -subprocess.CalledProcessError: Command '['docker', 'build', '-t', 'jupyterhub/k8s-hub:0.9-217f798', 'images/hub', '--build-arg', 'JUPYTERHUB_VERSION=git+https://github.com/jupyterhub/jupyterhub@master']' returned non-zero exit status 100. +subprocess.CalledProcessError: Command '['docker', 'build', '-t', 'quay.io/jupyterhub/k8s-hub:0.9-217f798', 'images/hub', '--build-arg', 'JUPYTERHUB_VERSION=git+https://github.com/jupyterhub/jupyterhub@master']' returned non-zero exit status 100. # while installing a dependency for our k8s cluster Unable to connect to the server: dial tcp: lookup docs.projectcalico.org on 127.0.0.53:53: read udp 127.0.0.1:56409->127.0.0.53:53: i/o timeout diff --git a/RELEASE.md b/RELEASE.md index d4a4b40ad6..fb6053979f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -28,7 +28,7 @@ Also consider nudging dependent projects in the JupyterHub GitHub organization f These images version/tags are set in [values.yaml](jupyterhub/values.yaml), consider bumping the version of these as well. - [ ] [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) - - [Available image tags](https://hub.docker.com/r/jupyterhub/configurable-http-proxy/tags) + - [Available image tags](https://quay.io/repository/jupyterhub/configurable-http-proxy?tab=tags) - values.yaml entry: `proxy.chp.image` - [ ] [traefik/traefik](https://github.com/traefik/traefik) - [Available image tags](https://hub.docker.com/_/traefik?tab=tags) diff --git a/docs/source/administrator/security.md b/docs/source/administrator/security.md index 56edc67f56..9b34b041e7 100644 --- a/docs/source/administrator/security.md +++ b/docs/source/administrator/security.md @@ -61,7 +61,7 @@ changes to your `config.yaml` file: **NOTE:** -If the proxy service is of type `LoadBalancer`, which it is by default, then a specific static IP address can be requested (if available) instead of a dynamically acquired one. +If the proxy service is of type `LoadBalancer`, which it is by default, then a specific static IP address can be requested (if available) instead of a dynamically acquired one. Although not essential for HTTPS, using a static IP address is a recommended practice for domain names referencing fixed IPs. This ensures the same IP address for multiple deployments. The IP can be provided like: @@ -179,7 +179,7 @@ hub: # when debugging something from the hub pod. To use it, apply this # configuration. # - name: jupyterhub/k8s-hub-slim + name: quay.io/jupyterhub/k8s-hub-slim ``` ```{note} diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index 7fe8c64c8f..4440654a3f 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -8,7 +8,7 @@ Services can be run [externally](https://jupyterhub.readthedocs.io/en/stable/get ## Hub-managed services in z2jh -A Hub-managed service will run in the same container/pod as the Hub itself. First, you'll need to install or copy the appropriate files for the service into your Hub image, either by creating a custom image derived from [`jupyterhub/k8s-hub`](https://hub.docker.com/r/jupyterhub/k8s-hub) or the [hub.extraFiles](schema_hub.extraFiles) configuration. Keep in mind that your Hub container may need to install dependency libraries like flask or fastapi, depending on the service. In those cases, you'll need a custom image. +A Hub-managed service will run in the same container/pod as the Hub itself. First, you'll need to install or copy the appropriate files for the service into your Hub image, either by creating a custom image derived from [`jupyterhub/k8s-hub`](https://quay.io/repository/jupyterhub/k8s-hub) or the [hub.extraFiles](schema_hub.extraFiles) configuration. Keep in mind that your Hub container may need to install dependency libraries like flask or fastapi, depending on the service. In those cases, you'll need a custom image. In addition to the code for the service, you need to modify the Hub Kubernetes Service object to include [multiple ports](https://kubernetes.io/docs/concepts/services-networking/service/#multi-port-services), and update the Hub Network Policy. If you want to allow access from all sources, you can use [hub.networkPolicy.allowedIngressPorts](schema_hub.networkPolicy.allowedIngressPorts). Otherwise if you want to more precisely control access, you can use [hub.networkPolicy.ingress](schema_hub.networkPolicy.ingress). @@ -19,7 +19,7 @@ In the following snippet, I'm using a custom image that copies over the applicat ```Dockerfile # Dockerfile # 2.0.0 is latest stable release at the time of this writing -FROM jupyterhub/k8s-hub:2.0.0 +FROM quay.io/jupyterhub/k8s-hub:2.0.0 # Depending on version, the k8s-hub image may have installed # pip packages as root, forcing you to install as root as well diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index 83f5871e5d..7b9376c75c 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -33,7 +33,7 @@ RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ # The final stage - slim version # ------------------------------ -# This stage is built and published as jupyterhub/k8s-hub-slim. It is meant to +# This stage is built and published as quay.io/jupyterhub/k8s-hub-slim. It is meant to # provide no non-essential packages. # FROM python:3.11-slim-bullseye as slim-stage diff --git a/images/singleuser-sample/README.md b/images/singleuser-sample/README.md index d3b03cb07d..1a6e692a89 100644 --- a/images/singleuser-sample/README.md +++ b/images/singleuser-sample/README.md @@ -17,10 +17,10 @@ To quickly try out this Docker image on your computer: ```sh # with JupyterLab -docker run -it --rm -p 8888:8888 jupyterhub/k8s-singleuser-sample:2.0.0 -- jupyter lab --ip 0.0.0.0 +docker run -it --rm -p 8888:8888 quay.io/jupyterhub/k8s-singleuser-sample:2.0.0 -- jupyter lab --ip 0.0.0.0 ``` -This image available tags can be found [here](https://hub.docker.com/r/jupyterhub/k8s-singleuser-sample/tags/). +This image available tags can be found [here](https://quay.io/repository/jupyterhub/k8s-singleuser-sample?tab=tags). ## In the base-notebook image From 624bc181c432de20a0c027cbc992d172f2b29d80 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 11:43:47 +0530 Subject: [PATCH 856/898] Actually change the network-tools image location --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 2f7eac35cc..2f1a58ad4b 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -344,7 +344,7 @@ singleuser: preferred: [] networkTools: image: - name: jupyterhub/k8s-network-tools + name: quay.io/jupyterhub/k8s-network-tools tag: "set-by-chartpress" pullPolicy: pullSecrets: [] From 6aa6d5f6532600f1c3ab3e9f05d48767908e023e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 12:05:55 +0530 Subject: [PATCH 857/898] Output logs from all pods if tests fail --- .circleci/config.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 87016b1591..8f92e4fbde 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -71,7 +71,11 @@ jobs: export KUBECONFIG="$HOME/.kube/config" export HUB_URL=http://localhost:30080 . ./ci/common - pytest --verbose --color=yes ./tests -m 'not netpol' + # Print out logs from all pods if the tests fail + pytest --verbose --color=yes ./tests -m 'not netpol' || \ + kubectl get pod -o name | \ + xargs -I {} -L1 /bin/bash -c \ + "echo Logs for {} && kubectl logs --all-containers {} && echo --------------" name: Run tests - run: From db30e92ba62e28c628db32e3f214a6d08240307e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 12:16:34 +0530 Subject: [PATCH 858/898] Account for CircleCI having GNU `xargs` --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8f92e4fbde..51ad688b86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -74,7 +74,7 @@ jobs: # Print out logs from all pods if the tests fail pytest --verbose --color=yes ./tests -m 'not netpol' || \ kubectl get pod -o name | \ - xargs -I {} -L1 /bin/bash -c \ + xargs -I {} /bin/bash -c \ "echo Logs for {} && kubectl logs --all-containers {} && echo --------------" name: Run tests From 13291561f4dcd12598417eb79e6bb7d266400228 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 12:24:31 +0530 Subject: [PATCH 859/898] Print more debug info from pods when CircleCI test fails --- .circleci/config.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 51ad688b86..a664182417 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -71,11 +71,15 @@ jobs: export KUBECONFIG="$HOME/.kube/config" export HUB_URL=http://localhost:30080 . ./ci/common - # Print out logs from all pods if the tests fail + # Print out logs & definition info from all pods if the tests fail pytest --verbose --color=yes ./tests -m 'not netpol' || \ kubectl get pod -o name | \ xargs -I {} /bin/bash -c \ - "echo Logs for {} && kubectl logs --all-containers {} && echo --------------" + "echo Logs for {} && \ + kubectl get {} -o yaml && \ + kubectl describe {} && \ + kubectl logs --all-containers {} && \ + echo --------------------------------" name: Run tests - run: From a9f3c592fbd25211cad7f06f51a9a4c5fe9bb1be Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 18 Oct 2023 13:19:24 +0530 Subject: [PATCH 860/898] Document quay.io as a place to push repo2docker built images --- docs/source/repo2docker.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/source/repo2docker.md b/docs/source/repo2docker.md index ef370c9a9a..c43a8a4d26 100644 --- a/docs/source/repo2docker.md +++ b/docs/source/repo2docker.md @@ -72,9 +72,7 @@ to configure JupyterHub to build off of this image: 4. **Get credentials for a docker repository.** The image you will build for your JupyterHub must be made available by being - published to some container registry. You could for example use [Docker Hub](https://hub.docker.com/) or [Google Container Registry](https://cloud.google.com/artifact-registry). - - + published to some container registry. You could for example use [quay.io](https://quay.io) or [Docker Hub](https://hub.docker.com/). In the next step, you need an image reference for you and others to find your image with. @@ -85,10 +83,10 @@ to configure JupyterHub to build off of this image: /: ``` - An image reference on Google Container Registry: + An image reference on quay.io: ``` - gcr.io//: + quay.io//: ``` - Your image name can be anything memorable. From 3711dad3bd3ebd9cf2129304f457ba7db1767865 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 19 Oct 2023 05:09:25 +0000 Subject: [PATCH 861/898] Update kube-scheduler version from v1.26.9 to v1.26.10 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 740b53e521..c93a001122 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -519,7 +519,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.9" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.10" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 9d58b71f7c4661afb72afc7320aacb8a7c9e8ff4 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 20 Oct 2023 15:47:52 +0530 Subject: [PATCH 862/898] Use full form of -y in sample dockerfile This should also just trigger a rebuild of the image in CI --- images/singleuser-sample/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/singleuser-sample/Dockerfile b/images/singleuser-sample/Dockerfile index dc4da26bc1..9c7e73850c 100644 --- a/images/singleuser-sample/Dockerfile +++ b/images/singleuser-sample/Dockerfile @@ -45,8 +45,8 @@ RUN adduser \ ${NB_USER} RUN apt-get update \ - && apt-get upgrade -y \ - && apt-get install -y --no-install-recommends \ + && apt-get upgrade --yes \ + && apt-get install --yes --no-install-recommends \ ca-certificates \ dnsutils \ iputils-ping \ From 6cfacd4b03d0ae9badee102487d838c690ef9b27 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Fri, 20 Oct 2023 18:38:33 +0530 Subject: [PATCH 863/898] Add changelog entry --- docs/source/changelog.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 69f27d7131..0474dde370 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,6 +12,42 @@ changes in pull requests], this list should be updated. [development releases]: https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub [breaking changes in pull requests]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking +### Default image registry moved to [quay.io](https://quay.io) + +We have moved the registry where we publish our docker images from [dockerhub](https://hub.docker.com) +to [quay.io](https://quay.io). This move is to ensure our users are not [throttled by dockerhub](https://docs.docker.com/docker-hub/download-rate-limit/), +and us maintainers don't have to apply for 'sponsored OSS Project' from docker each year. This +should have no material impact on your experience. + +For the benefit of people running older versions of z2jh and are throttled by DockerHub, +we have actually copied all our *released* images from dockerhub to quay.io as well. +So you can opt in to using the images from quay.io with the following config: + +```yaml +hub: + image: + name: quay.io/jupyterhub/k8s-hub +proxy: + chp: + image: + name: quay.io/jupyterhub/configurable-http-proxy + secretSync: + image: + name: quay.io/jupyterhub/k8s-secret-sync +singleuser: + networkTools: + image: + name: quay.io/jupyterhub/k8s-network-tools +prePuller: + hook: + image: + name: quay.io/jupyterhub/k8s-image-awaiter +``` + +You don't have to explicitly specify the tag, as the existing tags +will work. Note that this **only** works for *released* versions of +z2jh - if you are using a *dev* version of z2jh, this will not work. + ## 3.1 ### 3.1.0 - 2023-09-29 From feccd75638593a86b9e8171fd5864f0c878c34a0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 22 Oct 2023 23:22:24 +0200 Subject: [PATCH 864/898] ci: fetch stable/dev releases using helm show to avoid cache issues Previously we fetched the latest stable and dev release from metadata we have maintained, but doing that and then using `helm install` of that version made us use two sources of data, where one could be relatively outdated due to hitting a cache while the other may not. Using `helm show`, we rely on the helm chart repository website returning a index.yaml listing versions etc both when checking whats the latest versions, and when installing - like that we avoid a cache issue I think. --- .github/workflows/test-chart.yaml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 1e3d23fb40..d869446f04 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -279,8 +279,16 @@ jobs: if: matrix.test == 'upgrade' run: | . ./ci/common - if [ ${{ matrix.upgrade-from }} = stable -o ${{ matrix.upgrade-from }} = dev ]; then - UPGRADE_FROM_VERSION=$(curl -sSL https://hub.jupyter.org/helm-chart/info.json | jq -er '.jupyterhub.${{ matrix.upgrade-from }}') + # NOTE: We change the directory so jupyterhub the chart name won't be + # misunderstood as the local folder name. + # + # https://github.com/helm/helm/issues/9244 + cd ci + + if [ ${{ matrix.upgrade-from }} = stable ]; then + UPGRADE_FROM_VERSION=$(helm show chart --repo=https://hub.jupyter.org/helm-chart/ jupyterhub | yq e '.version' -) + elif [ ${{ matrix.upgrade-from }} = dev ]; then + UPGRADE_FROM_VERSION=$(helm show chart --devel --repo=https://hub.jupyter.org/helm-chart/ jupyterhub | yq e '.version' -) else UPGRADE_FROM_VERSION=${{ matrix.upgrade-from }} fi @@ -289,11 +297,6 @@ jobs: echo "" echo "Installing already released jupyterhub version $UPGRADE_FROM_VERSION" - # FIXME: We change the directory so jupyterhub the chart name won't be - # misunderstood as the local folder name. - # - # https://github.com/helm/helm/issues/9244 - cd ci helm install jupyterhub --repo https://hub.jupyter.org/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} - name: "(Upgrade) Install helm diff" From 4060b6a1324b0f44fc5b9126fccaa3a6d3504853 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 22 Oct 2023 23:33:29 +0200 Subject: [PATCH 865/898] ci: cleanup unused sourcing of ci/common --- .github/workflows/test-chart.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index d869446f04..20be8e62a5 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -278,7 +278,6 @@ jobs: - name: "(Upgrade) Install ${{ matrix.upgrade-from }} chart" if: matrix.test == 'upgrade' run: | - . ./ci/common # NOTE: We change the directory so jupyterhub the chart name won't be # misunderstood as the local folder name. # From 611d57896715a822ab5dcd019f415012d20c7ccc Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 25 Oct 2023 23:38:27 +0530 Subject: [PATCH 866/898] Bump to newest tag --- docs/source/administrator/services.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/administrator/services.md b/docs/source/administrator/services.md index 4440654a3f..bd75524625 100644 --- a/docs/source/administrator/services.md +++ b/docs/source/administrator/services.md @@ -18,8 +18,9 @@ In the following snippet, I'm using a custom image that copies over the applicat ```Dockerfile # Dockerfile -# 2.0.0 is latest stable release at the time of this writing -FROM quay.io/jupyterhub/k8s-hub:2.0.0 +# 3.1.0 is latest stable release at the time of this writing +# Find all tags in https://quay.io/repository/jupyterhub/k8s-hub?tab=tags +FROM quay.io/jupyterhub/k8s-hub:3.1.0 # Depending on version, the k8s-hub image may have installed # pip packages as root, forcing you to install as root as well From bdfbbf6ea62125cbdbfbe4afbef296f52fa2fd52 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 25 Oct 2023 23:38:43 +0530 Subject: [PATCH 867/898] Fix casing to be consistent --- docs/source/changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 0474dde370..23fe9cdcfa 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -19,7 +19,7 @@ to [quay.io](https://quay.io). This move is to ensure our users are not [throttl and us maintainers don't have to apply for 'sponsored OSS Project' from docker each year. This should have no material impact on your experience. -For the benefit of people running older versions of z2jh and are throttled by DockerHub, +For the benefit of people running older versions of z2jh and are throttled by dockerhub, we have actually copied all our *released* images from dockerhub to quay.io as well. So you can opt in to using the images from quay.io with the following config: From 7d877f9b50da2d124c31852e18c9e4399564b4e0 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Wed, 25 Oct 2023 23:40:03 +0530 Subject: [PATCH 868/898] Satisfy the absolute *worst* part of precommit And the yaml indent, which is much better --- docs/source/changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 23fe9cdcfa..d72aa48cc2 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -20,7 +20,7 @@ and us maintainers don't have to apply for 'sponsored OSS Project' from docker e should have no material impact on your experience. For the benefit of people running older versions of z2jh and are throttled by dockerhub, -we have actually copied all our *released* images from dockerhub to quay.io as well. +we have actually copied all our _released_ images from dockerhub to quay.io as well. So you can opt in to using the images from quay.io with the following config: ```yaml @@ -33,7 +33,7 @@ proxy: name: quay.io/jupyterhub/configurable-http-proxy secretSync: image: - name: quay.io/jupyterhub/k8s-secret-sync + name: quay.io/jupyterhub/k8s-secret-sync singleuser: networkTools: image: @@ -45,8 +45,8 @@ prePuller: ``` You don't have to explicitly specify the tag, as the existing tags -will work. Note that this **only** works for *released* versions of -z2jh - if you are using a *dev* version of z2jh, this will not work. +will work. Note that this **only** works for _released_ versions of +z2jh - if you are using a _dev_ version of z2jh, this will not work. ## 3.1 From e7ce26ed456b77f998a9f74e3351dab2f4f1bc53 Mon Sep 17 00:00:00 2001 From: Andrii Salnikov Date: Wed, 25 Oct 2023 23:26:43 +0200 Subject: [PATCH 869/898] add profile with profile_options to lint-and-validate values --- tools/templates/lint-and-validate-values.yaml | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 0db90333c7..0507edf346 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -344,7 +344,27 @@ singleuser: mock description 2 kubespawner_override: image: XYZ:XYZ - nodeSelector: + - display_name: "mock display name 3" + description: | + mock description 3 + profile_options: + image: + display_name: Image + choices: + image1: + display_name: "image 1" + kubespawner_override: + image: XXX:XXX + image2: + display_name: "image 2" + kubespawner_override: + image: YYY:YYY + unlisted_choice: + enabled: true + display_name: "image ZZZ tag" + kubespawner_override: + image: "ZZZ:{value}" + nodeSelector: mock-node-selector: mock extraTolerations: - effect: NoSchedule From d6569c932b43b9f28cc6823799f7dc4ae36fbfc2 Mon Sep 17 00:00:00 2001 From: Andrii Salnikov Date: Wed, 25 Oct 2023 23:31:37 +0200 Subject: [PATCH 870/898] add profile with profile_options to lint-and-validate values (fix typo) --- tools/templates/lint-and-validate-values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 0507edf346..69d7b4534d 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -364,7 +364,7 @@ singleuser: display_name: "image ZZZ tag" kubespawner_override: image: "ZZZ:{value}" - nodeSelector: + nodeSelector: mock-node-selector: mock extraTolerations: - effect: NoSchedule From aa009e2de79ce2a8c18580a75959cbd63725697c Mon Sep 17 00:00:00 2001 From: Raniere Silva Date: Fri, 27 Oct 2023 14:22:52 +0200 Subject: [PATCH 871/898] Move note box to before list of cloud providers. --- docs/source/kubernetes/setup-kubernetes.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md index c7e00d68d6..248c76e5dd 100644 --- a/docs/source/kubernetes/setup-kubernetes.md +++ b/docs/source/kubernetes/setup-kubernetes.md @@ -6,6 +6,12 @@ Kubernetes' documentation describes the many [ways to set up a cluster][ways to We attempt to provide quick instructions for the most painless and popular ways of setting up a Kubernetes cluster on various cloud providers and on other infrastructure. +```{note} +During the process of setting up JupyterHub, you'll be creating some +files for configuration purposes. It may be helpful to create a folder +for your JuypterHub deployment to keep track of these files. +``` + Choose one option and proceed. ```{toctree} @@ -22,10 +28,4 @@ ovh/step-zero-ovh other-infrastructure/step-zero-other ``` -```{note} -During the process of setting up JupyterHub, you'll be creating some -files for configuration purposes. It may be helpful to create a folder -for your JuypterHub deployment to keep track of these files. -``` - [ways to set up a cluster]: https://kubernetes.io/docs/setup/ From 1eed6df18759fc583b37978dd53393e0dcbcdfda Mon Sep 17 00:00:00 2001 From: Raniere Silva Date: Fri, 27 Oct 2023 15:06:27 +0200 Subject: [PATCH 872/898] Add setup for minikube For people that are learning about BinderHub --- .../kubernetes/minikube/step-zero-minikube.md | 46 +++++++++++++++++++ docs/source/kubernetes/setup-kubernetes.md | 1 + 2 files changed, 47 insertions(+) create mode 100644 docs/source/kubernetes/minikube/step-zero-minikube.md diff --git a/docs/source/kubernetes/minikube/step-zero-minikube.md b/docs/source/kubernetes/minikube/step-zero-minikube.md new file mode 100644 index 0000000000..3e987a16be --- /dev/null +++ b/docs/source/kubernetes/minikube/step-zero-minikube.md @@ -0,0 +1,46 @@ +(minikube-k8s) + +# Kubernetes on minikube (for development only) + +[minikube](minikube) is a implementation of Kubernetes as a local cluster primarily target to the development of applications. + +```{important} +The Zero to JupyterHub guide assumes you're using a managed Kubernetes service with one of the main cloud platforms and **[minikube](minikube) is not officially supported**. You may be able to get help on the [Jupyter community forum](https://discourse.jupyter.org/c/jupyterhub/10). +``` + +## Kubernetes cluster requirements + +All the requirements are implemented in minikube >= v1.31.2: + +- [Dynamic Volume Provisioning](https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/) for persistent storage +- [LoadBalancer](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer) or [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) for managing external access to JupyterHub + +## minikube installation + +Follow the installation steps in the [minikube's "Get Started!" page](https://minikube.sigs.k8s.io/docs/start/). + +## Kubernetes cluster creation + +From a terminal, run + +```bash +minikube start \ +--kubernetes-version v1.26.1 \ +--nodes 2 \ +--cpus 2 \ +--memory 2000 \ +--cni calico +``` + +To test if your cluster is initialized, run: + +``` +kubectl get node +``` + +The response should list two running nodes (or however many nodes you set with ``--nodes` above). + +Congrats. Now that you have your Kubernetes cluster running, it's time to +begin {ref}`setup-helm`. + +[minikube]: https://minikube.sigs.k8s.io/docs/ \ No newline at end of file diff --git a/docs/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md index c7e00d68d6..3ad79d1169 100644 --- a/docs/source/kubernetes/setup-kubernetes.md +++ b/docs/source/kubernetes/setup-kubernetes.md @@ -11,6 +11,7 @@ Choose one option and proceed. ```{toctree} :titlesonly: +step-zero-minikube google/step-zero-gcp microsoft/step-zero-azure amazon/step-zero-aws From 4487f6ceb63e54e11aa046dabbeaf081c6c095c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 13:08:03 +0000 Subject: [PATCH 873/898] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/source/kubernetes/minikube/step-zero-minikube.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/kubernetes/minikube/step-zero-minikube.md b/docs/source/kubernetes/minikube/step-zero-minikube.md index 3e987a16be..95cbe17b18 100644 --- a/docs/source/kubernetes/minikube/step-zero-minikube.md +++ b/docs/source/kubernetes/minikube/step-zero-minikube.md @@ -43,4 +43,4 @@ The response should list two running nodes (or however many nodes you set with ` Congrats. Now that you have your Kubernetes cluster running, it's time to begin {ref}`setup-helm`. -[minikube]: https://minikube.sigs.k8s.io/docs/ \ No newline at end of file +[minikube]: https://minikube.sigs.k8s.io/docs/ From 8605fe8022627a0abc582f976837b8e3a8f5931b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 05:20:26 +0000 Subject: [PATCH 874/898] build(deps): bump aquasecurity/trivy-action from 0.12.0 to 0.13.1 Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.12.0 to 0.13.1. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/fbd16365eb88e12433951383f5e99bd901fc618f...f78e9ecf42a1271402d4f484518b9313235990e1) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/vuln-scan.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vuln-scan.yaml b/.github/workflows/vuln-scan.yaml index 5b38191869..c7acb67b33 100644 --- a/.github/workflows/vuln-scan.yaml +++ b/.github/workflows/vuln-scan.yaml @@ -87,7 +87,7 @@ jobs: # Action reference: https://github.com/aquasecurity/trivy-action - name: Scan latest published image id: scan_1 - uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f + uses: aquasecurity/trivy-action@f78e9ecf42a1271402d4f484518b9313235990e1 with: image-ref: ${{ steps.image.outputs.spec }} format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -112,7 +112,7 @@ jobs: - name: Scan rebuilt image id: scan_2 if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f + uses: aquasecurity/trivy-action@f78e9ecf42a1271402d4f484518b9313235990e1 with: image-ref: rebuilt-image format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json @@ -171,7 +171,7 @@ jobs: - name: Describe vulnerabilities if: steps.rebuild.outcome == 'success' - uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f + uses: aquasecurity/trivy-action@f78e9ecf42a1271402d4f484518b9313235990e1 with: image-ref: rebuilt-image format: table From 7760ca6fb6b1f50db4fa6195153088dad3255191 Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Thu, 2 Nov 2023 15:28:07 +0530 Subject: [PATCH 875/898] Fix capitalization Co-authored-by: Ayaz Salikhov --- docs/source/changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index d72aa48cc2..56aeaf73e2 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,14 +14,14 @@ changes in pull requests], this list should be updated. ### Default image registry moved to [quay.io](https://quay.io) -We have moved the registry where we publish our docker images from [dockerhub](https://hub.docker.com) -to [quay.io](https://quay.io). This move is to ensure our users are not [throttled by dockerhub](https://docs.docker.com/docker-hub/download-rate-limit/), +We have moved the registry where we publish our docker images from [Docker Hub](https://hub.docker.com) +to [Quay.io](https://quay.io). This move is to ensure our users are not [throttled by Docker Hub](https://docs.docker.com/docker-hub/download-rate-limit/), and us maintainers don't have to apply for 'sponsored OSS Project' from docker each year. This should have no material impact on your experience. For the benefit of people running older versions of z2jh and are throttled by dockerhub, -we have actually copied all our _released_ images from dockerhub to quay.io as well. -So you can opt in to using the images from quay.io with the following config: +we have actually copied all our _released_ images from Docker Hub to Quay.io as well. +So you can opt in to using the images from Quay.io with the following config: ```yaml hub: From 44c393aa7cdddaca0d5b885ddfde8bf2e4e7ef8e Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 6 Nov 2023 05:13:01 +0000 Subject: [PATCH 876/898] Patch known vulnerability in secret-sync --- images/secret-sync/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/secret-sync/Dockerfile b/images/secret-sync/Dockerfile index 63590a9267..09f8398867 100644 --- a/images/secret-sync/Dockerfile +++ b/images/secret-sync/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-alpine -# VULN_SCAN_TIME=2023-10-16_05:13:02 +# VULN_SCAN_TIME=2023-11-06_05:12:59 # Note that we use tini-static, it embeds dependencies missing in alpine RUN ARCH=`uname -m`; \ From aa0a1c2dccd6a181fe83403eb867ab734bbccd99 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 20:28:07 +0000 Subject: [PATCH 877/898] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v3.14.0 → v3.15.0](https://github.com/asottile/pyupgrade/compare/v3.14.0...v3.15.0) - [github.com/psf/black: 23.9.1 → 23.10.1](https://github.com/psf/black/compare/23.9.1...23.10.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0a443bd99a..57ee00ebd8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Autoformat: Python code, syntax patterns are modernized - repo: https://github.com/asottile/pyupgrade - rev: v3.14.0 + rev: v3.15.0 hooks: - id: pyupgrade args: @@ -29,7 +29,7 @@ repos: # Autoformat: Python code - repo: https://github.com/psf/black - rev: 23.9.1 + rev: 23.10.1 hooks: - id: black args: From 08724ad4271d83d4b64eb1e8736a2b8c683fc713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 23:44:06 +0000 Subject: [PATCH 878/898] build(deps): bump aiohttp from 3.8.5 to 3.8.6 in /images/hub Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.8.5 to 3.8.6. - [Release notes](https://github.com/aio-libs/aiohttp/releases) - [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst) - [Commits](https://github.com/aio-libs/aiohttp/compare/v3.8.5...v3.8.6) --- updated-dependencies: - dependency-name: aiohttp dependency-type: indirect ... Signed-off-by: dependabot[bot] --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 3bc179f110..dbe94c3a8c 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.8.5 +aiohttp==3.8.6 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp From fd42b0d5f4436fe4228a16f5a6ed3b2959736f85 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 16 Nov 2023 05:09:37 +0000 Subject: [PATCH 879/898] Update kube-scheduler version from v1.26.10 to v1.26.11 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 2c95dec06d..365a35d264 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -519,7 +519,7 @@ scheduling: # here. We aim to stay around 1 minor version behind the latest k8s # version. # - tag: "v1.26.10" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG + tag: "v1.26.11" # ref: https://github.com/kubernetes/kubernetes/tree/master/CHANGELOG pullPolicy: pullSecrets: [] nodeSelector: {} From 63ec438319aeea4011a21b2be0f209def0c8540b Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Thu, 23 Nov 2023 11:10:08 +0000 Subject: [PATCH 880/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 57 +++++++-------- images/singleuser-sample/requirements.txt | 88 +++++++++++------------ 2 files changed, 68 insertions(+), 77 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index dbe94c3a8c..e0779ebc15 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,16 +4,14 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.8.6 +aiohttp==3.9.0 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp -alembic==1.12.0 +alembic==1.12.1 # via jupyterhub async-generator==1.10 # via jupyterhub -async-timeout==4.0.3 - # via aiohttp attrs==23.1.0 # via # aiohttp @@ -23,7 +21,7 @@ bcrypt==4.0.1 # via # jupyterhub-firstuseauthenticator # jupyterhub-nativeauthenticator -certifi==2023.7.22 +certifi==2023.11.17 # via # kubernetes-asyncio # requests @@ -31,11 +29,9 @@ certipy==0.1.3 # via jupyterhub cffi==1.16.0 # via cryptography -charset-normalizer==3.3.0 - # via - # aiohttp - # requests -cryptography==41.0.4 +charset-normalizer==3.3.2 + # via requests +cryptography==41.0.5 # via # pyjwt # pyopenssl @@ -47,7 +43,7 @@ frozenlist==1.4.0 # via # aiohttp # aiosignal -greenlet==3.0.0 +greenlet==3.0.1 # via sqlalchemy idna==3.4 # via @@ -57,11 +53,11 @@ jinja2==3.1.2 # via # jupyterhub # jupyterhub-kubespawner -jsonschema==4.19.1 +jsonschema==4.20.0 # via # jupyter-telemetry # oauthenticator -jsonschema-specifications==2023.7.1 +jsonschema-specifications==2023.11.1 # via jsonschema jupyter-telemetry==0.1.0 # via jupyterhub @@ -81,7 +77,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==6.1.0 +jupyterhub-kubespawner==6.2.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in @@ -91,11 +87,11 @@ jupyterhub-nativeauthenticator==1.2.0 # via -r requirements.in jupyterhub-tmpauthenticator==1.0.0 # via -r requirements.in -kubernetes-asyncio==27.6.0 +kubernetes-asyncio==28.2.1 # via jupyterhub-kubespawner ldap3==2.9.1 # via jupyterhub-ldapauthenticator -mako==1.2.4 +mako==1.3.0 # via alembic markupsafe==2.1.3 # via @@ -105,11 +101,11 @@ multidict==6.0.4 # via # aiohttp # yarl -mwoauth==0.3.8 +mwoauth==0.4.0 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.1.0 +oauthenticator==16.2.0 # via -r requirements.in oauthlib==3.2.2 # via @@ -123,11 +119,11 @@ packaging==23.2 # via jupyterhub pamela==1.1.0 # via jupyterhub -prometheus-client==0.17.1 +prometheus-client==0.19.0 # via jupyterhub -psycopg2==2.9.8 +psycopg2==2.9.9 # via -r requirements.in -pyasn1==0.5.0 +pyasn1==0.5.1 # via ldap3 pycparser==2.21 # via cffi @@ -140,7 +136,7 @@ pyjwt[crypto]==2.8.0 # mwoauth pymysql==1.1.0 # via -r requirements.in -pyopenssl==23.2.0 +pyopenssl==23.3.0 # via certipy python-dateutil==2.8.2 # via @@ -155,7 +151,7 @@ pyyaml==6.0.1 # via # jupyterhub-kubespawner # kubernetes-asyncio -referencing==0.30.2 +referencing==0.31.0 # via # jsonschema # jsonschema-specifications @@ -167,23 +163,22 @@ requests==2.31.0 # requests-oauthlib requests-oauthlib==1.3.1 # via mwoauth -rpds-py==0.10.3 +rpds-py==0.13.1 # via # jsonschema # referencing -ruamel-yaml==0.17.33 +ruamel-yaml==0.18.5 # via # jupyter-telemetry # oauthenticator -ruamel-yaml-clib==0.2.7 +ruamel-yaml-clib==0.2.8 # via ruamel-yaml six==1.16.0 # via # kubernetes-asyncio - # mwoauth # onetimepass # python-dateutil -sqlalchemy==2.0.21 +sqlalchemy==2.0.23 # via # alembic # jupyterhub @@ -200,7 +195,7 @@ tornado==6.3.3 # jupyterhub-idle-culler # jupyterhub-ldapauthenticator # oauthenticator -traitlets==5.10.1 +traitlets==5.13.0 # via # jupyter-telemetry # jupyterhub @@ -212,12 +207,12 @@ typing-extensions==4.8.0 # via # alembic # sqlalchemy -urllib3==2.0.7 +urllib3==2.1.0 # via # jupyterhub-kubespawner # kubernetes-asyncio # requests -yarl==1.9.2 +yarl==1.9.3 # via aiohttp # The following packages are considered to be unsafe in a requirements file: diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index b828ae9bca..04d9324165 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -4,9 +4,9 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -alembic==1.12.0 +alembic==1.12.1 # via jupyterhub -anyio==4.0.0 +anyio==4.1.0 # via jupyter-server argon2-cffi==23.1.0 # via @@ -16,7 +16,7 @@ argon2-cffi-bindings==21.2.0 # via argon2-cffi arrow==1.3.0 # via isoduration -asttokens==2.4.0 +asttokens==2.4.1 # via stack-data async-generator==1.10 # via jupyterhub @@ -26,15 +26,13 @@ attrs==23.1.0 # via # jsonschema # referencing -babel==2.13.0 +babel==2.13.1 # via jupyterlab-server -backcall==0.2.0 - # via ipython beautifulsoup4==4.12.2 # via nbconvert -bleach==6.0.0 +bleach==6.1.0 # via nbconvert -certifi==2023.7.22 +certifi==2023.11.17 # via requests certipy==0.1.3 # via jupyterhub @@ -42,11 +40,11 @@ cffi==1.16.0 # via # argon2-cffi-bindings # cryptography -charset-normalizer==3.3.0 +charset-normalizer==3.3.2 # via requests -comm==0.1.4 +comm==0.2.0 # via ipykernel -cryptography==41.0.4 +cryptography==41.0.5 # via pyopenssl debugpy==1.8.0 # via ipykernel @@ -54,24 +52,24 @@ decorator==5.1.1 # via ipython defusedxml==0.7.1 # via nbconvert -executing==2.0.0 +executing==2.0.1 # via stack-data -fastjsonschema==2.18.1 +fastjsonschema==2.19.0 # via nbformat fqdn==1.5.1 # via jsonschema -greenlet==3.0.0 +greenlet==3.0.1 # via sqlalchemy idna==3.4 # via # anyio # jsonschema # requests -ipykernel==6.25.2 +ipykernel==6.27.0 # via # jupyterlab # nbclassic -ipython==8.16.1 +ipython==8.17.2 # via ipykernel ipython-genutils==0.2.0 # via nbclassic @@ -91,21 +89,21 @@ json5==0.9.14 # via jupyterlab-server jsonpointer==2.4 # via jsonschema -jsonschema[format-nongpl]==4.19.1 +jsonschema[format-nongpl]==4.20.0 # via # jupyter-events # jupyter-telemetry # jupyterlab-server # nbformat -jsonschema-specifications==2023.7.1 +jsonschema-specifications==2023.11.1 # via jsonschema -jupyter-client==8.3.1 +jupyter-client==8.6.0 # via # ipykernel # jupyter-server # nbclassic # nbclient -jupyter-core==5.3.2 +jupyter-core==5.5.0 # via # ipykernel # jupyter-client @@ -115,11 +113,11 @@ jupyter-core==5.3.2 # nbclient # nbconvert # nbformat -jupyter-events==0.7.0 +jupyter-events==0.9.0 # via jupyter-server jupyter-lsp==2.2.0 # via jupyterlab -jupyter-server==2.7.3 +jupyter-server==2.10.1 # via # jupyter-lsp # jupyterlab @@ -133,13 +131,13 @@ jupyter-telemetry==0.1.0 # via jupyterhub jupyterhub==4.0.2 # via -r requirements.in -jupyterlab==4.0.6 +jupyterlab==4.0.9 # via -r requirements.in -jupyterlab-pygments==0.2.2 +jupyterlab-pygments==0.3.0 # via nbconvert -jupyterlab-server==2.25.0 +jupyterlab-server==2.25.2 # via jupyterlab -mako==1.2.4 +mako==1.3.0 # via alembic markupsafe==2.1.3 # via @@ -154,9 +152,9 @@ mistune==3.0.2 # via nbconvert nbclassic==1.0.0 # via -r requirements.in -nbclient==0.8.0 +nbclient==0.9.0 # via nbconvert -nbconvert==7.8.0 +nbconvert==7.11.0 # via # jupyter-server # nbclassic @@ -196,18 +194,16 @@ parso==0.8.3 # via jedi pexpect==4.8.0 # via ipython -pickleshare==0.7.5 - # via ipython -platformdirs==3.11.0 +platformdirs==4.0.0 # via jupyter-core -prometheus-client==0.17.1 +prometheus-client==0.19.0 # via # jupyter-server # jupyterhub # nbclassic -prompt-toolkit==3.0.39 +prompt-toolkit==3.0.41 # via ipython -psutil==5.9.5 +psutil==5.9.6 # via ipykernel ptyprocess==0.7.0 # via @@ -217,11 +213,11 @@ pure-eval==0.2.2 # via stack-data pycparser==2.21 # via cffi -pygments==2.16.1 +pygments==2.17.2 # via # ipython # nbconvert -pyopenssl==23.2.0 +pyopenssl==23.3.0 # via certipy python-dateutil==2.8.2 # via @@ -240,7 +236,7 @@ pyzmq==25.1.1 # jupyter-client # jupyter-server # nbclassic -referencing==0.30.2 +referencing==0.31.0 # via # jsonschema # jsonschema-specifications @@ -257,13 +253,13 @@ rfc3986-validator==0.1.1 # via # jsonschema # jupyter-events -rpds-py==0.10.3 +rpds-py==0.13.1 # via # jsonschema # referencing -ruamel-yaml==0.17.33 +ruamel-yaml==0.18.5 # via jupyter-telemetry -ruamel-yaml-clib==0.2.7 +ruamel-yaml-clib==0.2.8 # via ruamel-yaml send2trash==1.8.2 # via @@ -279,13 +275,13 @@ sniffio==1.3.0 # via anyio soupsieve==2.5 # via beautifulsoup4 -sqlalchemy==2.0.21 +sqlalchemy==2.0.23 # via # alembic # jupyterhub stack-data==0.6.3 # via ipython -terminado==0.17.1 +terminado==0.18.0 # via # jupyter-server # jupyter-server-terminals @@ -302,7 +298,7 @@ tornado==6.3.3 # nbclassic # nbgitpuller # terminado -traitlets==5.10.1 +traitlets==5.13.0 # via # comm # ipykernel @@ -327,9 +323,9 @@ typing-extensions==4.8.0 # sqlalchemy uri-template==1.3.0 # via jsonschema -urllib3==2.0.7 +urllib3==2.1.0 # via requests -wcwidth==0.2.8 +wcwidth==0.2.12 # via prompt-toolkit webcolors==1.13 # via jsonschema @@ -337,5 +333,5 @@ webencodings==0.5.1 # via # bleach # tinycss2 -websocket-client==1.6.3 +websocket-client==1.6.4 # via jupyter-server From fe43c02f6fbd5165e8e64f9a04e1f67017e50b3c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Nov 2023 12:54:20 +0100 Subject: [PATCH 881/898] docs: note that images under profile_options are pulled also --- docs/source/conf.py | 1 + jupyterhub/values.schema.yaml | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index c2ce1a2196..3a05d33dc1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -175,6 +175,7 @@ def parse_schema(d, md=[], depth=0, pre=""): intersphinx_mapping = { "jupyterhub": ("https://jupyterhub.readthedocs.io/en/stable/", None), "oauthenticator": ("https://oauthenticator.readthedocs.io/en/stable/", None), + "kubespawner": ("https://jupyterhub-kubespawner.readthedocs.io/en/stable/", None), } # intersphinx_disabled_reftypes set based on recommendation in diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 6f9dd314ff..69c13a83c0 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -2850,9 +2850,12 @@ properties: pullProfileListImages: type: boolean description: | - The singleuser.profileList configuration can let the user choose an - image through the selection of a profile. This option determines if - those images will be pulled, both by the hook and continuous pullers. + The singleuser.profileList configuration can provide a selection of + images. This option determines if all images identified there should + be pulled, both by the hook and continuous pullers. + + Images are looked for under `kubespawner_override`, and also + `profile_options.choices.kubespawner_override` since version 3.2.0. The reason to disable this, is that if you have for example 10 images which start pulling in order from 1 to 10, a user that arrives and From 322d6ae6f286091922e4c25581f2bdc535158b55 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Nov 2023 13:03:00 +0100 Subject: [PATCH 882/898] Apply suggestions from code review --- docs/source/kubernetes/minikube/step-zero-minikube.md | 6 +++--- docs/source/kubernetes/setup-kubernetes.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/kubernetes/minikube/step-zero-minikube.md b/docs/source/kubernetes/minikube/step-zero-minikube.md index 95cbe17b18..51f878b8a4 100644 --- a/docs/source/kubernetes/minikube/step-zero-minikube.md +++ b/docs/source/kubernetes/minikube/step-zero-minikube.md @@ -1,8 +1,8 @@ (minikube-k8s) -# Kubernetes on minikube (for development only) +# Kubernetes on minikube (for trial and development only) -[minikube](minikube) is a implementation of Kubernetes as a local cluster primarily target to the development of applications. +[minikube](minikube) can setup a Kubernetes cluster on a single computer. Using minikube can be suitable trial for new Kubernetes users and for development purposes, but its not meant to be used for production purposes. ```{important} The Zero to JupyterHub guide assumes you're using a managed Kubernetes service with one of the main cloud platforms and **[minikube](minikube) is not officially supported**. You may be able to get help on the [Jupyter community forum](https://discourse.jupyter.org/c/jupyterhub/10). @@ -25,7 +25,7 @@ From a terminal, run ```bash minikube start \ ---kubernetes-version v1.26.1 \ +--kubernetes-version stable \ --nodes 2 \ --cpus 2 \ --memory 2000 \ diff --git a/docs/source/kubernetes/setup-kubernetes.md b/docs/source/kubernetes/setup-kubernetes.md index 3ad79d1169..3bf7666380 100644 --- a/docs/source/kubernetes/setup-kubernetes.md +++ b/docs/source/kubernetes/setup-kubernetes.md @@ -11,7 +11,7 @@ Choose one option and proceed. ```{toctree} :titlesonly: -step-zero-minikube +minikube/step-zero-minikube google/step-zero-gcp microsoft/step-zero-azure amazon/step-zero-aws From a317c8ad41fd84a83ef9f5650b5cb900b65911b0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Nov 2023 13:10:58 +0100 Subject: [PATCH 883/898] docs: reference learning over trial, and fix broken link issue --- .../source/kubernetes/minikube/step-zero-minikube.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/kubernetes/minikube/step-zero-minikube.md b/docs/source/kubernetes/minikube/step-zero-minikube.md index 51f878b8a4..31f63f0b93 100644 --- a/docs/source/kubernetes/minikube/step-zero-minikube.md +++ b/docs/source/kubernetes/minikube/step-zero-minikube.md @@ -1,13 +1,15 @@ (minikube-k8s) -# Kubernetes on minikube (for trial and development only) +# Kubernetes on minikube (for learning and development only) -[minikube](minikube) can setup a Kubernetes cluster on a single computer. Using minikube can be suitable trial for new Kubernetes users and for development purposes, but its not meant to be used for production purposes. +[minikube] can setup a Kubernetes cluster on a single computer. minikube be suitable in order to learn about Kubernetes and to develop and test changes, but its not meant to be used for production purposes. ```{important} -The Zero to JupyterHub guide assumes you're using a managed Kubernetes service with one of the main cloud platforms and **[minikube](minikube) is not officially supported**. You may be able to get help on the [Jupyter community forum](https://discourse.jupyter.org/c/jupyterhub/10). +The Zero to JupyterHub guide assumes you're using a managed Kubernetes service with one of the main cloud platforms and **[minikube] is not officially supported**. You may be able to get help on the [Jupyter community forum](https://discourse.jupyter.org/c/jupyterhub/10). ``` +[minikube]: https://minikube.sigs.k8s.io/docs/ + ## Kubernetes cluster requirements All the requirements are implemented in minikube >= v1.31.2: @@ -34,7 +36,7 @@ minikube start \ To test if your cluster is initialized, run: -``` +```bash kubectl get node ``` @@ -42,5 +44,3 @@ The response should list two running nodes (or however many nodes you set with ` Congrats. Now that you have your Kubernetes cluster running, it's time to begin {ref}`setup-helm`. - -[minikube]: https://minikube.sigs.k8s.io/docs/ From 656d7c0c056f5ba4590a2be5cc924726f8b69bd1 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Nov 2023 15:28:49 +0100 Subject: [PATCH 884/898] pre-commit: fix note about shellsheck --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 57ee00ebd8..f4c3222be2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ # # https://github.com/koalaman/shellcheck#installing # -# pre-commit run --config .pre-commit-config-shellcheck.yaml --all-files +# pre-commit run --hook-stage=manual shellcheck --all-files # repos: # Autoformat: Python code, syntax patterns are modernized @@ -37,6 +37,7 @@ repos: - --target-version=py39 - --target-version=py310 - --target-version=py311 + - --target-version=py312 # Autoformat: Python code - repo: https://github.com/pycqa/isort From 88c30f488b7d03627a12417b2e11a1bfdde257e0 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Thu, 23 Nov 2023 18:54:41 +0100 Subject: [PATCH 885/898] Publish to Docker Hub alongside Quay.io --- .github/workflows/publish.yml | 1 + ci/publish | 12 ++++++---- docs/source/changelog.md | 44 ++++++++++------------------------- 3 files changed, 21 insertions(+), 36 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4d3f01cbc0..2fb2b73f54 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -109,6 +109,7 @@ jobs: if: steps.publishing.outputs.publishing run: | docker login -u "${{ secrets.QUAY_USERNAME }}" -p "${{ secrets.QUAY_PASSWORD }}" quay.io + docker login -u "${{ secrets.DOCKER_USERNAME }}" -p "${{ secrets.DOCKER_PASSWORD }}" docker.io - name: Configure a git user # Having a user.email and user.name configured with git is required to diff --git a/ci/publish b/ci/publish index a045fdd7c3..63fa73b6e2 100755 --- a/ci/publish +++ b/ci/publish @@ -6,10 +6,10 @@ # Exit on errors, assert env vars, log commands set -eux -PUBLISH_ARGS="--push --publish-chart \ +PUBLISH_ARGS="--push \ --builder docker-buildx \ --platform linux/amd64 --platform linux/arm64 \ - " +" # chartpress use git to push to our Helm chart repository, which is the gh-pages # branch of jupyterhub/helm-chart. We have installed a private SSH key within @@ -33,13 +33,17 @@ if [[ $GITHUB_REF != refs/tags/* ]]; then EXTRA_MESSAGE="${GITHUB_REPOSITORY}${PR_OR_HASH} ${LATEST_COMMIT_TITLE}" # shellcheck disable=SC2086 - chartpress $PUBLISH_ARGS --extra-message "${EXTRA_MESSAGE}" + chartpress $PUBLISH_ARGS --extra-message "${EXTRA_MESSAGE}" --publish-chart + # shellcheck disable=SC2086 + chartpress $PUBLISH_ARGS --extra-message "${EXTRA_MESSAGE}" --image-prefix=jupyterhub/k8s- else # Setting a tag explicitly enforces a rebuild if this tag had already been # built and we wanted to override it. # shellcheck disable=SC2086 - chartpress $PUBLISH_ARGS --tag "${GITHUB_REF:10}" + chartpress $PUBLISH_ARGS --tag "${GITHUB_REF:10}" --publish-chart + # shellcheck disable=SC2086 + chartpress $PUBLISH_ARGS --tag "${GITHUB_REF:10}" --image-prefix=jupyterhub/k8s- fi # Let us log the changes chartpress did, it should include replacements for diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 56aeaf73e2..6500401fff 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,41 +12,21 @@ changes in pull requests], this list should be updated. [development releases]: https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub [breaking changes in pull requests]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking -### Default image registry moved to [quay.io](https://quay.io) +### Default image registry changed to Quay.io -We have moved the registry where we publish our docker images from [Docker Hub](https://hub.docker.com) -to [Quay.io](https://quay.io). This move is to ensure our users are not [throttled by Docker Hub](https://docs.docker.com/docker-hub/download-rate-limit/), -and us maintainers don't have to apply for 'sponsored OSS Project' from docker each year. This -should have no material impact on your experience. +We now publish the chart's docker images to both [Quay.io] and [Docker Hub] and +the chart is from now configured to use the images at Quay.io by default. +Previous releases of images (excluding pre-releases) has been copied over to +Quay.io as well. -For the benefit of people running older versions of z2jh and are throttled by dockerhub, -we have actually copied all our _released_ images from Docker Hub to Quay.io as well. -So you can opt in to using the images from Quay.io with the following config: +The change is to ensure that images can be pulled without a [Docker Hub rate +limit] even if the [JupyterHub organization on Docker Hub] wouldn't be sponsored +by Docker Hub in the future, something we need to apply for each year. -```yaml -hub: - image: - name: quay.io/jupyterhub/k8s-hub -proxy: - chp: - image: - name: quay.io/jupyterhub/configurable-http-proxy - secretSync: - image: - name: quay.io/jupyterhub/k8s-secret-sync -singleuser: - networkTools: - image: - name: quay.io/jupyterhub/k8s-network-tools -prePuller: - hook: - image: - name: quay.io/jupyterhub/k8s-image-awaiter -``` - -You don't have to explicitly specify the tag, as the existing tags -will work. Note that this **only** works for _released_ versions of -z2jh - if you are using a _dev_ version of z2jh, this will not work. +[docker hub]: https://hub.docker.com +[docker hub rate limit]: https://docs.docker.com/docker-hub/download-rate-limit/ +[jupyterhub organization on docker hub]: https://hub.docker.com/u/jupyterhub +[quay.io]: https://quay.io ## 3.1 From 9f485428f6ff0381b6972b92c17615906250a697 Mon Sep 17 00:00:00 2001 From: JupterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:03:58 +0000 Subject: [PATCH 886/898] Update jupyterhub/configurable-http-proxy version from 4.6.0 to 4.6.1 --- jupyterhub/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 365a35d264..4f361470d8 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -205,7 +205,7 @@ proxy: # tag is automatically bumped to new patch versions by the # watch-dependencies.yaml workflow. # - tag: "4.6.0" # https://github.com/jupyterhub/configurable-http-proxy/tags + tag: "4.6.1" # https://github.com/jupyterhub/configurable-http-proxy/tags pullPolicy: pullSecrets: [] extraCommandLineFlags: [] From be618072da046afdd01c671aceef959c42cac01f Mon Sep 17 00:00:00 2001 From: JupyterHub Bot Account <105740858+jupyterhub-bot@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:04:25 +0000 Subject: [PATCH 887/898] hub image: refreeze requirements.txt --- images/hub/requirements.txt | 4 ++-- images/singleuser-sample/requirements.txt | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index e0779ebc15..7de50eef98 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -4,7 +4,7 @@ # # Use the "Run workflow" button at https://github.com/jupyterhub/zero-to-jupyterhub-k8s/actions/workflows/watch-dependencies.yaml # -aiohttp==3.9.0 +aiohttp==3.9.1 # via kubernetes-asyncio aiosignal==1.3.1 # via aiohttp @@ -45,7 +45,7 @@ frozenlist==1.4.0 # aiosignal greenlet==3.0.1 # via sqlalchemy -idna==3.4 +idna==3.6 # via # requests # yarl diff --git a/images/singleuser-sample/requirements.txt b/images/singleuser-sample/requirements.txt index 04d9324165..efbd361c7d 100644 --- a/images/singleuser-sample/requirements.txt +++ b/images/singleuser-sample/requirements.txt @@ -60,16 +60,16 @@ fqdn==1.5.1 # via jsonschema greenlet==3.0.1 # via sqlalchemy -idna==3.4 +idna==3.6 # via # anyio # jsonschema # requests -ipykernel==6.27.0 +ipykernel==6.26.0 # via # jupyterlab # nbclassic -ipython==8.17.2 +ipython==8.18.1 # via ipykernel ipython-genutils==0.2.0 # via nbclassic @@ -115,7 +115,7 @@ jupyter-core==5.5.0 # nbformat jupyter-events==0.9.0 # via jupyter-server -jupyter-lsp==2.2.0 +jupyter-lsp==2.2.1 # via jupyterlab jupyter-server==2.10.1 # via @@ -192,7 +192,7 @@ pandocfilters==1.5.0 # via nbconvert parso==0.8.3 # via jedi -pexpect==4.8.0 +pexpect==4.9.0 # via ipython platformdirs==4.0.0 # via jupyter-core From d49c8cc5ad355f1fd6c0b27ba1cc47cfc9d6bb34 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 14:31:30 +0100 Subject: [PATCH 888/898] Add changelog for 3.2.0 --- docs/source/changelog.md | 45 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 6500401fff..ee39f8d88c 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -12,7 +12,19 @@ changes in pull requests], this list should be updated. [development releases]: https://hub.jupyter.org/helm-chart/#development-releases-jupyterhub [breaking changes in pull requests]: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pulls?q=is%3Apr+is%3Aclosed+label%3Abreaking -### Default image registry changed to Quay.io +## 3.2 + +### 3.2.0 - 2023-11-27 + +```{warning} If you are upgrading from 3.0.x +A bug in KubeSpawner 5.0-6.0 present in z2jh 3.0.0-3.0.3 made user server pods +risk be orphaned by JupyterHub, making them run indefinitely and cause +unnecessary cloud costs. + +Read more about how to clean up these user server pods in [this forum post]. +``` + +#### Default image registry changed to Quay.io We now publish the chart's docker images to both [Quay.io] and [Docker Hub] and the chart is from now configured to use the images at Quay.io by default. @@ -28,6 +40,37 @@ by Docker Hub in the future, something we need to apply for each year. [jupyterhub organization on docker hub]: https://hub.docker.com/u/jupyterhub [quay.io]: https://quay.io +#### Enhancements made + +- Pull images from `singleuser.profileList` found in `profile_options.choices` [#3217](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3217) ([@manfuin](https://github.com/manfuin), [@consideRatio](https://github.com/consideRatio), [@yuvipanda](https://github.com/yuvipanda)) + +#### Maintenance and upkeep improvements + +- Update jupyterhub/configurable-http-proxy version from 4.6.0 to 4.6.1 [#3275](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3275) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) +- Publish to Docker Hub alongside Quay.io [#3272](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3272) ([@consideRatio](https://github.com/consideRatio), [@minrk](https://github.com/minrk)) +- Update oauthenticator from 16.1.1 to 16.2.0, kubespawner from 6.1.0 to 6.2.0, and kubernetes-asyncio from 27.6.0 to 28.2.1 [#3270](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3270) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) +- Update kube-scheduler version from v1.26.9 to v1.26.11 [#3269](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3269), [#3255](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3255) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) +- Use quay.io as source of docker images [#3254](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3254) ([@yuvipanda](https://github.com/yuvipanda), [@minrk](https://github.com/minrk), [@manics](https://github.com/manics), [@mathbunnyru](https://github.com/mathbunnyru)) +- Update library/traefik version from v2.10.4 to v2.10.5 [#3248](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3248) ([@jupyterhub-bot](https://github.com/jupyterhub-bot), [@consideRatio](https://github.com/consideRatio)) + +#### Documentation improvements + +- Document k8s cluster setup using minikube (for learning and development) [#3260](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3260) ([@rgaiacs](https://github.com/rgaiacs), [@consideRatio](https://github.com/consideRatio)) +- Move note box to before list of cloud providers. [#3259](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3259) ([@rgaiacs](https://github.com/rgaiacs), [@consideRatio](https://github.com/consideRatio)) + +#### Continuous integration improvements + +- ci: fetch stable/dev releases using helm show to avoid cache issues [#3256](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3256) ([@consideRatio](https://github.com/consideRatio)) + +#### Contributors to this release + +The following people contributed discussions, new ideas, code and documentation contributions, and review. +See [our definition of contributors](https://github-activity.readthedocs.io/en/latest/#how-does-this-tool-define-contributions-in-the-reports). + +([GitHub contributors page for this release](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/graphs/contributors?from=2023-09-29&to=2023-11-27&type=c)) + +@consideRatio ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3AconsideRatio+updated%3A2023-09-29..2023-11-27&type=Issues)) | @elferherrera ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aelferherrera+updated%3A2023-09-29..2023-11-27&type=Issues)) | @jupyterhub-bot ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ajupyterhub-bot+updated%3A2023-09-29..2023-11-27&type=Issues)) | @manfuin ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanfuin+updated%3A2023-09-29..2023-11-27&type=Issues)) | @manics ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amanics+updated%3A2023-09-29..2023-11-27&type=Issues)) | @mathbunnyru ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Amathbunnyru+updated%3A2023-09-29..2023-11-27&type=Issues)) | @minrk ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Aminrk+updated%3A2023-09-29..2023-11-27&type=Issues)) | @rgaiacs ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Argaiacs+updated%3A2023-09-29..2023-11-27&type=Issues)) | @vizeit ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Avizeit+updated%3A2023-09-29..2023-11-27&type=Issues)) | @yuvipanda ([activity](https://github.com/search?q=repo%3Ajupyterhub%2Fzero-to-jupyterhub-k8s+involves%3Ayuvipanda+updated%3A2023-09-29..2023-11-27&type=Issues)) + ## 3.1 ### 3.1.0 - 2023-09-29 From 06538920dea2758393dab45135a6407f706dff14 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 15:00:02 +0100 Subject: [PATCH 889/898] Bump to 3.2.0 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index a0ce33efe6..99cfcdf4de 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.1.1-0.dev" + baseVersion: "3.2.0" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index c3babaaddd..d8104fe617 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.1.1-0.dev" +current = "3.2.0" # match our prerelease prefixes # -alpha.1 From f2b80466dbf1aa37b802a0a3fbf5603486ae75be Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 15:00:31 +0100 Subject: [PATCH 890/898] Bump to 3.2.1-0.dev --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 99cfcdf4de..57a141ebac 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.2.0" + baseVersion: "3.2.1-0.dev" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index d8104fe617..31b57c75e1 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.2.0" +current = "3.2.1-0.dev" # match our prerelease prefixes # -alpha.1 From 9dd926fce0bd0ce6c35f09bdfe1704941af4410c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 18:41:32 +0100 Subject: [PATCH 891/898] Update oauthenticator from 16.2.0 to 16.2.1 --- images/hub/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index 7de50eef98..6013770ca5 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -105,7 +105,7 @@ mwoauth==0.4.0 # via -r requirements.in nullauthenticator==1.0.0 # via -r requirements.in -oauthenticator==16.2.0 +oauthenticator==16.2.1 # via -r requirements.in oauthlib==3.2.2 # via From 45d094bc9ef5c1ddb4cff6e93745ee347093b336 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 18:44:34 +0100 Subject: [PATCH 892/898] Add changelog for 3.2.1 --- docs/source/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/changelog.md b/docs/source/changelog.md index ee39f8d88c..2a7fc07d23 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -14,6 +14,12 @@ changes in pull requests], this list should be updated. ## 3.2 +### 3.2.1 - 2023-11-27 + +#### Maintenance and upkeep improvements + +- Update oauthenticator from 16.2.0 to 16.2.1 [#3278](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/pull/3278) ([@consideRatio](https://github.com/consideRatio)) + ### 3.2.0 - 2023-11-27 ```{warning} If you are upgrading from 3.0.x From c0a696f56433be64ac85a3bc6e0bff329285e80a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 27 Nov 2023 18:47:14 +0100 Subject: [PATCH 893/898] Bump to 3.2.1 --- chartpress.yaml | 2 +- tbump.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chartpress.yaml b/chartpress.yaml index 57a141ebac..6739be75b0 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.2.1-0.dev" + baseVersion: "3.2.1" repo: git: jupyterhub/helm-chart published: https://jupyterhub.github.io/helm-chart diff --git a/tbump.toml b/tbump.toml index 31b57c75e1..560f5d28af 100644 --- a/tbump.toml +++ b/tbump.toml @@ -5,7 +5,7 @@ # Config reference: https://github.com/your-tools/tbump#readme # [version] -current = "3.2.1-0.dev" +current = "3.2.1" # match our prerelease prefixes # -alpha.1 From 523b509d8337b6113a68be2051d889f2d5fab2cb Mon Sep 17 00:00:00 2001 From: Shota Matsumoto Date: Tue, 5 Dec 2023 15:47:22 +0900 Subject: [PATCH 894/898] Stop to require apiToken when schedulableNotebook and usersExporter are disabled --- jupyterhub/templates/hub/secret.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jupyterhub/templates/hub/secret.yaml b/jupyterhub/templates/hub/secret.yaml index 5e5885f834..b169be6eb8 100644 --- a/jupyterhub/templates/hub/secret.yaml +++ b/jupyterhub/templates/hub/secret.yaml @@ -11,12 +11,16 @@ data: {{- $_ := set $values "Chart" (dict "Name" .Chart.Name "Version" .Chart.Version) }} {{- $_ := set $values "Release" (pick .Release "Name" "Namespace" "Service") }} values.yaml: {{ $values | toYaml | b64enc | quote }} + {{- if .Values.schedulableNotebook.enabled }} {{- $schedulableNotebookToken := (required "Schedulable notebook token must be a 32 byte random string generated with `openssl rand -hex 32`!" .Values.schedulableNotebook.secretToken) | b64enc | quote }} schedulable-notebook.token: {{ $schedulableNotebookToken }} hub.services.schedulable-notebook.apiToken: {{ $schedulableNotebookToken }} + {{- end }} + {{- if .Values.usersExporter.enabled }} {{- $usersExporterToken := (required "Users exporter token must be a 32 byte random string generated with `openssl rand -hex 32`!" .Values.usersExporter.secretToken) | b64enc | quote }} users-exporter.token: {{ $usersExporterToken }} hub.services.users-exporter.apiToken: {{ $usersExporterToken }} + {{- end }} gcs.bucketName: {{ .Values.googleCloudStorage.bucket | b64enc | quote }} googleCloudServiceAccount.json: {{ .Values.googleCloudServiceAccount.json | b64enc | quote }} grafana.apiKey: {{ .Values.grafana.apiKey | b64enc | quote }} From 5291b8eaa4c2822469a61c68490bef252319f28f Mon Sep 17 00:00:00 2001 From: iguchi82 Date: Thu, 1 Feb 2024 11:51:37 +0900 Subject: [PATCH 895/898] Modifications to suit the RCOS environment --- .github/workflows/test-chart.yaml | 97 ++++++++++++----------- chartpress.yaml | 2 +- dev-config.yaml | 1 - images/hub/Dockerfile | 18 ++++- images/hub/requirements.txt | 2 +- jupyterhub/Chart.yaml | 2 +- jupyterhub/files/hub/jupyterhub_config.py | 37 +++++---- jupyterhub/templates/hub/rollout.yaml | 2 +- jupyterhub/values.schema.yaml | 17 ++++ jupyterhub/values.yaml | 12 +-- 10 files changed, 117 insertions(+), 73 deletions(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index bb8f4e1b04..7655dbd2a2 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -129,12 +129,12 @@ jobs: # k3s-channel: https://update.k3s.io/v1-release/channels # include: - - k3s-channel: latest + - k3s-channel: v1.29.0+k3s1 test: install - k3s-channel: stable # also test hub-slim, and prePuller.hook test: install local-chart-extra-args: >- - --set hub.image.name=quay.io/jupyterhub/k8s-hub-slim + --set hub.image.name=gcr.io/nii-ap-ops/k8s-hub --set prePuller.hook.enabled=true --set prePuller.hook.pullOnlyOnChanges=true - k3s-channel: v1.26 # also test hub.existingSecret @@ -145,7 +145,6 @@ jobs: --set hub.cookieSecret=bbbb2222 --set hub.config.CryptKeeper.keys[0]=cccc3333 create-k8s-test-resources: true - # We run three upgrade tests where we first install an already released # Helm chart version and then upgrade to the version we are now # testing: @@ -173,44 +172,46 @@ jobs: --set hub.db.type=sqlite-pvc --set singleuser.storage.type=dynamic create-k8s-test-resources: true - - k3s-channel: v1.24 - test: upgrade - upgrade-from: dev - upgrade-from-extra-args: >- - --set proxy.secretToken=aaaa1111 - --set hub.db.type=sqlite-pvc - --set singleuser.storage.type=dynamic - local-chart-extra-args: >- - --set hub.db.type=sqlite-pvc - --set singleuser.storage.type=dynamic - - k3s-channel: v1.23 - test: upgrade - # We're testing hub.db.upgrade with PostgreSQL so this version must be old - # enough to require a DB upgrade - upgrade-from: 2.0.0 - upgrade-from-extra-args: >- - --set proxy.secretToken=aaaa1111 - --set hub.cookieSecret=bbbb2222 - --set hub.config.CryptKeeper.keys[0]=cccc3333 - --set hub.db.type=postgres - --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub - --set singleuser.storage.type=dynamic - local-chart-extra-args: >- - --set hub.db.type=postgres - --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub - --set singleuser.storage.type=dynamic - --set hub.db.upgrade=true - create-k8s-test-resources: true - # https://artifacthub.io/packages/helm/bitnami/postgresql - setup-postgresql-args: >- - --version=11.6.13 - --set auth.enablePostgresUser=true - --set auth.postgresPassword=postgres - --set auth.database=jupyterhub - --set audit.logHostname=true - --set audit.logConnections=true - --set audit.logDisconnections=true - --set audit.clientMinMessages=debug + # v1.24はEOLとなっており、利用想定がないのでテストを除外する + # - k3s-channel: v1.24 + # test: upgrade + # upgrade-from: dev + # upgrade-from-extra-args: >- + # --set proxy.secretToken=aaaa1111 + # --set hub.db.type=sqlite-pvc + # --set singleuser.storage.type=dynamic + # local-chart-extra-args: >- + # --set hub.db.type=sqlite-pvc + # --set singleuser.storage.type=dynamic + # RCOSのリポジトリにjupyterhub chart version 2.0.0がないため、テストを除外する + # - k3s-channel: v1.23 + # test: upgrade + # # We're testing hub.db.upgrade with PostgreSQL so this version must be old + # # enough to require a DB upgrade + # upgrade-from: 2.0.0 + # upgrade-from-extra-args: >- + # --set proxy.secretToken=aaaa1111 + # --set hub.cookieSecret=bbbb2222 + # --set hub.config.CryptKeeper.keys[0]=cccc3333 + # --set hub.db.type=postgres + # --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub + # --set singleuser.storage.type=dynamic + # local-chart-extra-args: >- + # --set hub.db.type=postgres + # --set hub.db.url=postgresql+psycopg2://postgres:postgres@postgresql:5432/jupyterhub + # --set singleuser.storage.type=dynamic + # --set hub.db.upgrade=true + # create-k8s-test-resources: true + # # https://artifacthub.io/packages/helm/bitnami/postgresql + # setup-postgresql-args: >- + # --version=11.6.13 + # --set auth.enablePostgresUser=true + # --set auth.postgresPassword=postgres + # --set auth.database=jupyterhub + # --set audit.logHostname=true + # --set audit.logConnections=true + # --set audit.logDisconnections=true + # --set audit.clientMinMessages=debug steps: - uses: actions/checkout@v4 @@ -242,7 +243,7 @@ jobs: helm install pebble --repo https://hub.jupyter.org/helm-chart/ pebble --values dev-config-pebble.yaml # Install CRD for Argo Rollouts - - name: Install Argo Aollouts + - name: Install Argo Rollouts run: | kubectl create namespace argo-rollouts kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml @@ -291,9 +292,9 @@ jobs: cd ci if [ ${{ matrix.upgrade-from }} = stable ]; then - UPGRADE_FROM_VERSION=$(helm show chart --repo=https://hub.jupyter.org/helm-chart/ jupyterhub | yq e '.version' -) + UPGRADE_FROM_VERSION=$(helm show chart --repo=https://rcosdp.github.io/CS-jhub-helm-chart/ jupyterhub | yq e '.version' -) elif [ ${{ matrix.upgrade-from }} = dev ]; then - UPGRADE_FROM_VERSION=$(helm show chart --devel --repo=https://hub.jupyter.org/helm-chart/ jupyterhub | yq e '.version' -) + UPGRADE_FROM_VERSION=$(helm show chart --devel --repo=https://rcosdp.github.io/CS-jhub-helm-chart/ jupyterhub | yq e '.version' -) else UPGRADE_FROM_VERSION=${{ matrix.upgrade-from }} fi @@ -302,7 +303,7 @@ jobs: echo "" echo "Installing already released jupyterhub version $UPGRADE_FROM_VERSION" - helm install jupyterhub --repo https://hub.jupyter.org/helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} + helm install jupyterhub --repo https://rcosdp.github.io/CS-jhub-helm-chart/ jupyterhub --values ../dev-config.yaml --version=$UPGRADE_FROM_VERSION ${{ matrix.upgrade-from-extra-args }} - name: "(Upgrade) Install helm diff" if: matrix.test == 'upgrade' @@ -365,6 +366,10 @@ jobs: timeout: 150 max-restarts: 1 + - name: "Sleep 60sec to await hub" + run: | + sleep 60 + - name: Await local chart cert acquisition run: | . ./ci/common @@ -375,7 +380,7 @@ jobs: run: | . ./ci/common # If you have problems with the tests add '--capture=no' to show stdout - pytest --verbose --maxfail=2 --color=yes ./tests + pytest --verbose --maxfail=2 --color=yes --capture=no ./tests # ref: https://github.com/jupyterhub/action-k8s-namespace-report - name: Kubernetes namespace report diff --git a/chartpress.yaml b/chartpress.yaml index 019eaddc7f..2b5f0599b7 100644 --- a/chartpress.yaml +++ b/chartpress.yaml @@ -20,7 +20,7 @@ charts: # # baseVersion should be managed via tbump, see RELEASE.md for details # - baseVersion: "3.2.1" + baseVersion: "3.2.2" repo: git: RCOSDP/CS-jhub-helm-chart published: https://rcosdp.github.io/CS-jhub-helm-chart/ diff --git a/dev-config.yaml b/dev-config.yaml index 477aa560e3..1cf5f04e4f 100644 --- a/dev-config.yaml +++ b/dev-config.yaml @@ -145,7 +145,6 @@ hub: <<: *data-yaml mountPath: /etc/test/data.toml - schedulableNotebook: enabled: false secretToken: daad2e18301c37a14d0f6818b17283bbccc6c9637b652983dc5cfd538aba3326 # dummy diff --git a/images/hub/Dockerfile b/images/hub/Dockerfile index d5f31582d9..20a69439b8 100644 --- a/images/hub/Dockerfile +++ b/images/hub/Dockerfile @@ -20,7 +20,10 @@ FROM python:3.11-bullseye as build-stage # COPY requirements.txt requirements.txt ARG PIP_CACHE_DIR=/tmp/pip-cache +RUN apt-get update && apt-get install -y nodejs npm +RUN npm install --global yarn RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ + pip install --upgrade pip \ pip install build \ && pip wheel \ --wheel-dir=/tmp/wheels \ @@ -60,17 +63,26 @@ RUN apt-get update \ # requirement for using a local sqlite database sqlite3 \ tini \ + git \ + curl \ + gpg \ npm \ && rm -rf /var/lib/apt/lists/* # install wheels built in the build stage COPY requirements.txt /tmp/requirements.txt +RUN mkdir -m 0755 /etc/apt/keyrings +RUN apt-get update +RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg +RUN chmod a+r /etc/apt/keyrings/nodesource.gpg + ENV NODE_MAJOR=21 RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | \ - sudo tee /etc/apt/sources.list.d/nodesource.list -RUN sudo apt update -RUN sudo apt install -y nodejs + tee /etc/apt/sources.list.d/nodesource.list + +RUN apt update +RUN apt install -y nodejs RUN npm install --global yarn ARG PIP_CACHE_DIR=/tmp/pip-cache RUN --mount=type=cache,target=${PIP_CACHE_DIR} \ diff --git a/images/hub/requirements.txt b/images/hub/requirements.txt index f920b624ec..2a28f8b5a3 100644 --- a/images/hub/requirements.txt +++ b/images/hub/requirements.txt @@ -77,7 +77,7 @@ jupyterhub-hmacauthenticator==1.0 # via -r requirements.in jupyterhub-idle-culler==1.2.1 # via -r requirements.in -jupyterhub-kubespawner==6.2.0 +jupyterhub-kubespawner==5.0.0 # via -r requirements.in jupyterhub-ldapauthenticator==1.3.2 # via -r requirements.in diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml index 4e3180edde..17b1ada78b 100644 --- a/jupyterhub/Chart.yaml +++ b/jupyterhub/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: jupyterhub version: 0.0.1-set.by.chartpress -appVersion: "4.0.2" +appVersion: "3.1.1" description: Multi-user Jupyter installation keywords: [jupyter, jupyterhub, z2jh] home: https://z2jh.jupyter.org diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 70fca11a65..41987837a8 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -106,7 +106,7 @@ def camelCaseify(s): f'http://{get_name("hub")}:{get_name_env("hub", "_SERVICE_PORT")}' ) -env_url = os.environ.get('HUB_CONNECT_URL') +env_url = os.environ.get("HUB_CONNECT_URL") if env_url: c.JupyterHub.hub_connect_url = env_url @@ -360,19 +360,28 @@ def camelCaseify(s): get_config("singleuser.storage.extraVolumeMounts", []) ) -c.JupyterHub.services = [ - { - "name": "users-exporter", - "admin": True, - "api_token": get_secret_value(f"hub.services.users-exporter.apiToken"), - }, - { - "name": "schedulable-notebook", - "admin": True, - "url": "http://schedulable-notebook:8888", - "api_token": get_secret_value(f"hub.services.schedulable-notebook.apiToken"), - }, -] +c.JupyterHub.services = [] + + +if get_config("usersExporter.enabled", False): + c.JupyterHub.services.append( + { + "name": "users-exporter", + "admin": True, + "api_token": get_secret_value("hub.services.users-exporter.apiToken"), + } + ) + +if get_config("schedulableNotebook.enabled", False): + c.JupyterHub.services.append( + { + "name": "schedulable-notebook", + "admin": True, + "url": "http://schedulable-notebook:8888", + "api_token": get_secret_value("hub.services.schedulable-notebook.apiToken"), + }, + ) + c.JupyterHub.load_roles = [] # jupyterhub-idle-culler's permissions are scoped to what it needs only, see diff --git a/jupyterhub/templates/hub/rollout.yaml b/jupyterhub/templates/hub/rollout.yaml index e55882833b..07d42345aa 100644 --- a/jupyterhub/templates/hub/rollout.yaml +++ b/jupyterhub/templates/hub/rollout.yaml @@ -17,7 +17,7 @@ spec: blueGreen: previewService: {{ include "jupyterhub.hub.fullname" . }}-preview activeService: {{ include "jupyterhub.hub.fullname" . }} - autoPromotionEnabled: false + autoPromotionEnabled: {{ .Values.hub.rollout.autoPromotionEnabled }} template: metadata: labels: diff --git a/jupyterhub/values.schema.yaml b/jupyterhub/values.schema.yaml index 735ee9507a..cccc9b70a9 100644 --- a/jupyterhub/values.schema.yaml +++ b/jupyterhub/values.schema.yaml @@ -26,6 +26,8 @@ required: - debug - rbac - global + - usersExporter + - schedulableNotebook properties: enabled: type: [boolean, "null"] @@ -196,6 +198,13 @@ properties: additionalProperties: false required: [baseUrl] properties: + rollout: + type: object + properties: + autoPromotionEnabled: + type: boolean + description: | + Whether automatic promotion is enabled or not. revisionHistoryLimit: &revisionHistoryLimit type: [integer, "null"] minimum: 0 @@ -2721,6 +2730,10 @@ properties: usersExporter: type: object properties: + enabled: + type: boolean + description: | + Whether usersExporter is enabled or not. secretToken: type: [string, "null"] description: | @@ -2766,6 +2779,10 @@ properties: schedulableNotebook: type: object properties: + enabled: + type: boolean + description: | + Whether schedulableNotebook is enabled or not. secretToken: type: [string, "null"] description: | diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 67adcc4e42..c6da74b25c 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -32,6 +32,8 @@ imagePullSecrets: [] # the proxy pod. hub: revisionHistoryLimit: + rollout: + autoPromotionEnabled: true config: JupyterHub: admin_access: true @@ -75,7 +77,7 @@ hub: extraVolumes: [] extraVolumeMounts: [] image: - name: quay.io/jupyterhub/k8s-hub + name: gcr.io/nii-ap-ops/k8s-hub tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -341,7 +343,7 @@ proxy: runAsGroup: 65534 # nobody group allowPrivilegeEscalation: false image: - name: quay.io/jupyterhub/k8s-secret-sync + name: gcr.io/nii-ap-ops/k8s-secret-sync tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -381,7 +383,7 @@ singleuser: preferred: [] networkTools: image: - name: quay.io/jupyterhub/k8s-network-tools + name: gcr.io/nii-ap-ops/k8s-network-tools tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -433,7 +435,7 @@ singleuser: volumeNameTemplate: volume-{username}{servername} storageAccessModes: [ReadWriteOnce] image: - name: quay.io/jupyterhub/k8s-singleuser-sample + name: gcr.io/nii-ap-ops/k8s-singleuser-sample tag: "set-by-chartpress" pullPolicy: pullSecrets: [] @@ -642,7 +644,7 @@ prePuller: pullOnlyOnChanges: true # image and the configuration below relates to the hook-image-awaiter Job image: - name: quay.io/jupyterhub/k8s-image-awaiter + name: gcr.io/nii-ap-ops/k8s-image-awaiter tag: "set-by-chartpress" pullPolicy: pullSecrets: [] From 9d86d213bf1409e2819382e5469c2f1108c4b478 Mon Sep 17 00:00:00 2001 From: Shota Matsumoto Date: Thu, 1 Feb 2024 14:53:40 +0900 Subject: [PATCH 896/898] Remove redundant file: dev-config-upstream.yaml --- dev-config-upstream.yaml | 181 --------------------------------------- 1 file changed, 181 deletions(-) delete mode 100644 dev-config-upstream.yaml diff --git a/dev-config-upstream.yaml b/dev-config-upstream.yaml deleted file mode 100644 index 237ca4d683..0000000000 --- a/dev-config-upstream.yaml +++ /dev/null @@ -1,181 +0,0 @@ -proxy: - service: - type: NodePort - nodePorts: - http: 30080 - https: 30443 - https: - enabled: true - type: letsencrypt - hosts: - - local.jovyan.org - letsencrypt: - contactEmail: "jovyan@jupyter.test" - acmeServer: https://pebble/dir - traefik: - extraVolumes: - - name: pebble-root-cert - configMap: - name: pebble - extraVolumeMounts: - - name: pebble-root-cert - subPath: root-cert.pem - mountPath: /etc/pebble/root-cert.pem - extraEnv: - LEGO_CA_CERTIFICATES: /etc/pebble/root-cert.pem - chp: - resources: - requests: - memory: 0 - cpu: 0 - networkPolicy: - egress: [] # overrides allowance of 0.0.0.0/0 - -hub: - db: - type: sqlite-memory - services: - # The test service and its apiToken is used to make requests in our pytest - # suite of tests. Note that it can be overridden by the hub.existingSecret, - # which can cause tests to fail with 403. - test: - admin: true - apiToken: give-pytest-control - test-hub-existing-secret: - apiToken: dddd4444 - test-explicit-name: - name: some-explicitly-set-name - apiToken: eeee5555 - test-generation-of-apiToken: {} - networkPolicy: - egress: # overrides allowance of 0.0.0.0/0 - # In kind/k3s clusters the Kubernetes API server is exposing this port - - ports: - - protocol: TCP - port: 6443 - resources: - requests: - memory: 0 - cpu: 0 - extraConfig: - # emit logs associated with our pytest tests - test_hub_existing_secret: | - try: - from z2jh import get_secret_value - - print() - print("TEST LOGS: test_hub_existing_secret") - print(f"hub.existingSecret={get_config('hub.existingSecret')}") - print() - print(f"hub.db.type={get_config('hub.db.type')}") - print(f"MYSQL_PWD={os.environ.get('MYSQL_PWD', None)}") - print(f"PGPASSWORD={os.environ.get('PGPASSWORD', None)}") - print() - print(f"hub.services.test-hub-existing-secret.apiToken={get_secret_value('hub.services.test-hub-existing-secret.apiToken', None)}") - print() - print(f"CONFIGPROXY_AUTH_TOKEN={os.environ.get('CONFIGPROXY_AUTH_TOKEN', None)}") - print(f"hub.config.ConfigurableHTTPProxy.auth_token={get_secret_value('hub.config.ConfigurableHTTPProxy.auth_token', None)}") - print(f"hub.config.JupyterHub.cookie_secret={get_secret_value('hub.config.JupyterHub.cookie_secret', None)}") - print(f"hub.config.CryptKeeper.keys={get_secret_value('hub.config.CryptKeeper.keys', None)}") - print() - print(f"singleuser.extraLabels.test-chart-managed-secret={get_config('singleuser.extraLabels.test-chart-managed-secret')}") - print(f"singleuser.extraLabels.test-self-managed-secret={get_config('singleuser.extraLabels.test-self-managed-secret')}") - print("---") - print() - except ImportError: - print("WARNING: z2jh.py didn't contain get_secret_value") - - extraFiles: - my_config: - mountPath: /usr/local/etc/jupyterhub/jupyterhub_config.d/my_config.py - stringData: | - with open("/tmp/created-by-extra-files-config.txt", "w") as f: - f.write("hello world!") - binaryData1: &binaryData1 - mountPath: /tmp/binaryData.txt - mode: 0666 - binaryData: | - aGVsbG8gd - 29ybGQhCg== - binaryData2: &binaryData2 - mountPath: /tmp/dir1/binaryData.txt - mode: 0666 - binaryData: aGVsbG8gd29ybGQhCg== - stringData1: &stringData1 - mountPath: /tmp/stringData.txt - mode: 0666 - stringData: hello world! - stringData2: &stringData2 - mountPath: /tmp/dir1/stringData.txt - mode: 0666 - stringData: hello world! - data-yaml: &data-yaml - mountPath: /etc/test/data.yaml - mode: 0444 - data: - config: - map: - number: 123 - string: "hi" - list: [1, 2] - data-yml: &data-yml - <<: *data-yaml - mountPath: /etc/test/data.yml - data-json: &data-json - <<: *data-yaml - mountPath: /etc/test/data.json - data-toml: &data-toml - <<: *data-yaml - mountPath: /etc/test/data.toml - -singleuser: - extraLabels: - test-chart-managed-secret: "ok" - extraFiles: - binaryData1: *binaryData1 - binaryData2: *binaryData2 - stringData1: *stringData1 - stringData2: *stringData2 - data-yaml: *data-yaml - data-yml: *data-yml - data-json: *data-json - data-toml: *data-toml - storage: - type: none - memory: - guarantee: null - networkPolicy: - # For testing purposes in test_singleuser_netpol - egress: - - to: - # jupyter.org has multiple IPs associated with it, among them are these - # two. We explicitly allow access to one, but leave out the the other. - - ipBlock: - cidr: 104.21.25.233/32 - # - ipBlock: - # cidr: 172.67.134.225/32 - extraEnv: - TEST_ENV_FIELDREF_TO_NAMESPACE: - valueFrom: - fieldRef: - fieldPath: metadata.namespace - -prePuller: - hook: - enabled: false - -scheduling: - userScheduler: - enabled: true - replicas: 2 - # Can be set too low too not show scoring of scheduled pods, but also too - # high to show hex dumps of all network traffic. - # - # FIXME: Tweak this to include scoring, but exclude hex dumps. 10 includes - # hex dumps. 4 does not include scoring. Trying with 6... - logLevel: 6 - podPriority: - enabled: true - -debug: - enabled: true From f899b0d879937e37d6fac2e5d1102cf3ef2a3104 Mon Sep 17 00:00:00 2001 From: iguchi82 Date: Tue, 6 Feb 2024 20:43:19 +0900 Subject: [PATCH 897/898] In this Dockerfile, the Alpine base image has been changed to version 3.17.2, and the iptables package has been fixed to version 1.8.8. --- images/network-tools/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index bfe4aae199..a26fe0266d 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,5 @@ -FROM alpine:3 +FROM alpine:3.17.2 # VULN_SCAN_TIME=2023-06-19_05:12:47 -RUN apk add --no-cache iptables +RUN apk add --no-cache iptables~=1.8.8 From db4fb6237ab29a7c2eecb949bf909507c146c21a Mon Sep 17 00:00:00 2001 From: iguchi82 Date: Wed, 7 Feb 2024 12:11:40 +0900 Subject: [PATCH 898/898] Replace iptables with iptables-legacy in Dockerfile. --- images/network-tools/Dockerfile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/images/network-tools/Dockerfile b/images/network-tools/Dockerfile index a26fe0266d..508cc0712e 100644 --- a/images/network-tools/Dockerfile +++ b/images/network-tools/Dockerfile @@ -1,5 +1,11 @@ -FROM alpine:3.17.2 +FROM alpine:3 # VULN_SCAN_TIME=2023-06-19_05:12:47 -RUN apk add --no-cache iptables~=1.8.8 +#RUN apk add --no-cache iptables + +# Use iptables-legacy as workaround. +# cf. https://github.com/tailscale/tailscale/issues/10540 +RUN apk add --no-cache ca-certificates iptables iptables-legacy +RUN rm /sbin/iptables && ln -s /sbin/iptables-legacy /sbin/iptables +RUN rm /sbin/ip6tables && ln -s /sbin/ip6tables-legacy /sbin/ip6tables