From d05132c996c4d0d1b53108e50c6ef3a3033d2236 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 04:50:32 +0100 Subject: [PATCH 01/43] Add FIXME note about userPlaceholder resources --- .../templates/scheduling/user-placeholder/statefulset.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index 12f54be616..d33e274529 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -46,6 +46,7 @@ spec: - name: pause image: {{ .Values.prePuller.pause.image.name }}:{{ .Values.prePuller.pause.image.tag }} resources: + # FIXME: should render scheduling.userPlaceholder.resources if specified {{- include "jupyterhub.resources" . | nindent 12 }} {{- with .Values.scheduling.userPlaceholder.containerSecurityContext }} securityContext: From 7027dd65e63c930d492786fc5eed29c2a5c591cb Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 04:57:14 +0100 Subject: [PATCH 02/43] Remove unused hub.publicURL config --- jupyterhub/values.yaml | 1 - tools/templates/lint-and-validate-values.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 1bf10cb49e..c9128a4b64 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -38,7 +38,6 @@ hub: loadBalancerIP: baseUrl: / cookieSecret: - publicURL: initContainers: [] fsGid: 1000 nodeSelector: {} diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 87ca0d58a4..5d2903acb4 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -55,7 +55,6 @@ hub: ports: nodePort: baseUrl: / - publicURL: mock-public-url activeServerLimit: 3 deploymentStrategy: type: Recreate From 230c6bc669efef968c8188c54d5d0afa9fab9ab9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 04:57:38 +0100 Subject: [PATCH 03/43] Add scheduling.userPlaceholder.resources default value --- jupyterhub/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c9128a4b64..761639e570 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -443,6 +443,7 @@ scheduling: runAsUser: 65534 # nobody user runAsGroup: 65534 # nobody group allowPrivilegeEscalation: false + resources: {} corePods: nodeAffinity: matchNodePurpose: prefer From 0d6d8cc37fe7282e4aa26283b74d67b0c473fca7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 05:01:07 +0100 Subject: [PATCH 04/43] schema: don't require proxy.secretToken --- jupyterhub/schema.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 0aa40c9f4d..26df980984 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1244,8 +1244,6 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec - required: - - secretToken singleuser: type: object From 4e6e3ee65e9334e9743dc0cc52ec97bb217d1602 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 05:01:50 +0100 Subject: [PATCH 05/43] schema: remove entry about deprecated schedulerStrategy --- jupyterhub/schema.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 26df980984..9b2c399ca1 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1328,13 +1328,6 @@ properties: extra_resource_limits: nvidia.com/gpu: "1" ``` - schedulerStrategy: - type: - - string - - "null" - description: | - Deprecated and no longer does anything. Use the user-scheduler instead - in order to accomplish a good packing of the user pods. extraFiles: *extraFiles extraEnv: type: object From e03b247bc1a301f68d761b90dcd6114623f2b63f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 05:03:18 +0100 Subject: [PATCH 06/43] Add boilerplate schema entries with DESCRIBE ME comments The idea is to first bootstrap our schema with entries, and then do the work to describe all entries further for the configuration reference. --- jupyterhub/schema.yaml | 346 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 343 insertions(+), 3 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 9b2c399ca1..122ce86057 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,4 +1,11 @@ -title: Config +# Anchor candidates +# - resources +# - image +# - containerSecurityContext +# - livenessProbe +# - readinessProbe + +"$schema": http://json-schema.org/schema# type: object properties: fullnameOverride: @@ -48,12 +55,14 @@ properties: name: {{ include "jupyterhub.user-scheduler.fullname" . }} key: user-scheduler ``` + nameOverride: type: - string - "null" description: | See the documentation under [`fullnameOverride`](schema_fullnameOverride). + imagePullSecret: type: object description: | @@ -149,6 +158,9 @@ properties: Learn more in [this guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). + email: # DESCRIBE ME + type: string + imagePullSecrets: type: array description: | @@ -161,6 +173,7 @@ properties: You can use both the k8s native syntax, where each list element is like `{"name": "my-secret-name"}`, or you can let list elements be strings naming the secrets directly. + hub: type: object properties: @@ -350,7 +363,6 @@ properties: `subPath`](https://kubernetes.io/docs/concepts/storage/volumes/#secret) as is required to avoid replacing the content of the entire directory we mount in. - baseUrl: type: - string @@ -670,6 +682,16 @@ properties: - "null" description: | Size of disk to request for the database disk. + accessModes: # DESCRIBE ME + type: array + items: + type: string + storageClassName: # DESCRIBE ME + type: "null" + subPath: # DESCRIBE ME + type: "null" + upgrade: # DESCRIBE ME + type: "null" url: type: - string @@ -780,7 +802,10 @@ properties: uid: type: integer minimum: 0 - description: The UID the hub process should be running as. + description: | + DEPRECATED: Use 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! @@ -909,6 +934,81 @@ properties: See the [Kubernetes docs](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more info. + activeServerLimit: # DESCRIBE ME + type: "null" + allowNamedServers: # DESCRIBE ME + type: boolean + annotations: # DESCRIBE ME + type: object + authenticatePrometheus: # DESCRIBE ME + type: "null" + concurrentSpawnLimit: # DESCRIBE ME + type: integer + consecutiveFailureLimit: # DESCRIBE ME + type: integer + containerSecurityContext: # DESCRIBE ME + type: object + deploymentStrategy: # DESCRIBE ME + type: object + properties: + type: + type: string + extraConfigMap: # DESCRIBE ME + type: object + extraContainers: # DESCRIBE ME + type: array + extraVolumeMounts: # DESCRIBE ME + type: array + extraVolumes: # DESCRIBE ME + type: array + livenessProbe: # DESCRIBE ME + type: object + properties: + enabled: + type: boolean + failureThreshold: + type: integer + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + timeoutSeconds: + type: integer + namedServerLimitPerUser: # DESCRIBE ME + type: "null" + readinessProbe: # DESCRIBE ME + type: object + properties: + enabled: + type: boolean + failureThreshold: + type: integer + initialDelaySeconds: + type: integer + periodSeconds: + type: integer + timeoutSeconds: + type: integer + redirectToServer: # DESCRIBE ME + type: "null" + resources: # DESCRIBE ME + type: object + properties: + requests: + type: object + properties: + cpu: + type: string + memory: + type: string + services: # DESCRIBE ME + type: object + shutdownOnLogout: # DESCRIBE ME + type: "null" + templatePaths: # DESCRIBE ME + type: array + templateVars: # DESCRIBE ME + type: object proxy: type: object @@ -981,6 +1081,16 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + containerSecurityContext: # DESCRIBE ME + type: object + image: # DESCRIBE ME + type: object + livenessProbe: # DESCRIBE ME + type: object + readinessProbe: # DESCRIBE ME + type: object + resources: # DESCRIBE ME + type: object secretToken: type: - string @@ -1123,6 +1233,8 @@ properties: description: | The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. + acmeServer: # DESCRIBE ME + type: string manual: type: object description: | @@ -1244,6 +1356,51 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + containerSecurityContext: # DESCRIBE ME + type: object + extraDynamicConfig: # DESCRIBE ME + type: object + extraPorts: # DESCRIBE ME + type: array + extraStaticConfig: # DESCRIBE ME + type: object + extraVolumeMounts: # DESCRIBE ME + type: array + extraVolumes: # DESCRIBE ME + type: array + hsts: # DESCRIBE ME + type: object + properties: + includeSubdomains: + type: boolean + maxAge: + type: integer + preload: + type: boolean + image: # DESCRIBE ME + type: object + resources: # DESCRIBE ME + type: object + labels: # DESCRIBE ME + type: object + annotations: # DESCRIBE ME + type: object + deploymentStrategy: # DESCRIBE ME + type: object + properties: + rollingUpdate: + type: "null" + type: + type: string + secretSync: # DESCRIBE ME + type: object + properties: + containerSecurityContext: + type: object + image: + type: object + resources: + type: object singleuser: type: object @@ -1427,6 +1584,87 @@ properties: Pass this field an array of [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#weightedpodaffinityterm-v1-core) objects. + cloudMetadata: # DESCRIBE ME + type: object + properties: + blockWithIptables: + type: boolean + ip: + type: string + cmd: # DESCRIBE ME + type: string + defaultUrl: # DESCRIBE ME + type: "null" + events: # DESCRIBE ME + type: boolean + extraAnnotations: # DESCRIBE ME + type: object + extraContainers: # DESCRIBE ME + type: array + extraLabels: # DESCRIBE ME + type: object + properties: + hub.jupyter.org/network-access-hub: + type: string + extraPodConfig: # DESCRIBE ME + type: object + extraResource: # DESCRIBE ME + type: object + properties: + guarantees: + type: object + limits: + type: object + fsGid: # DESCRIBE ME + type: integer + lifecycleHooks: # DESCRIBE ME + type: object + networkTools: # DESCRIBE ME + type: object + properties: + image: + type: object + serviceAccountName: # DESCRIBE ME + type: "null" + startTimeout: # DESCRIBE ME + type: integer + storage: # DESCRIBE ME + type: object + properties: + capacity: + type: string + dynamic: + type: object + properties: + pvcNameTemplate: + type: string + storageAccessModes: + type: array + items: + type: string + storageClass: + type: "null" + volumeNameTemplate: + type: string + extraLabels: + type: object + extraVolumeMounts: + type: array + extraVolumes: + type: array + homeMountPath: + type: string + static: + type: object + properties: + pvcName: + type: "null" + subPath: + type: string + type: + type: string + uid: # DESCRIBE ME + type: integer scheduling: type: object @@ -1453,6 +1691,34 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + containerSecurityContext: # DESCRIBE ME + type: object + logLevel: # DESCRIBE ME + type: integer + plugins: # DESCRIBE ME + type: object + properties: + score: + type: object + properties: + disabled: + type: array + items: + type: object + properties: + name: + type: string + enabled: + type: array + items: + type: object + properties: + name: + type: string + weight: + type: integer + resources: # DESCRIBE ME + type: object podPriority: type: object description: | @@ -1541,6 +1807,8 @@ properties: description: | Unless specified here, the placeholder pods will request the same resources specified for the real singleuser pods. + containerSecurityContext: # DESCRIBE ME + type: object corePods: type: object description: | @@ -1593,6 +1861,7 @@ properties: ``` hub.jupyter.org/node-purpose=user ``` + ingress: type: object properties: @@ -1680,6 +1949,21 @@ properties: type: integer nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec + containerSecurityContext: # DESCRIBE ME + type: object + image: # DESCRIBE ME + type: object + properties: + name: + type: string + pullPolicy: + type: string + pullSecrets: + type: array + tag: + type: string + resources: # DESCRIBE ME + type: object continuous: description: | See the [*optimization @@ -1717,6 +2001,24 @@ properties: name: jupyter/all-spark-notebook tag: 2343e33dec46 ``` + containerSecurityContext: # DESCRIBE ME + type: object + pause: # DESCRIBE ME + type: object + properties: + containerSecurityContext: + type: object + image: + type: object + properties: + name: + type: string + pullPolicy: + type: string + pullSecrets: + type: array + tag: + type: string custom: type: object @@ -1734,3 +2036,41 @@ properties: myConfig.py: | c.MyAuthenticator.host = get_config("custom.myHost") ``` + + cull: # DESCRIBE ME + type: object + properties: + enabled: + type: boolean + users: + type: boolean + removeNamedServers: + type: boolean + timeout: + type: integer + every: + type: integer + concurrency: + type: integer + maxAge: + type: integer + + debug: + type: object + properties: + enabled: + type: boolean + description: | + Increases the loglevel throughout the resources in the Helm chart. + + rbac: # DESCRIBE ME + type: object + properties: + enabled: + type: boolean + + global: # DESCRIBE ME + type: object + properties: + safeToShowValues: + type: boolean From 8bf079a1d43487446cd4b509a391ba2bce55e403 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 16:47:19 +0100 Subject: [PATCH 07/43] Specify a specific json schema version --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 122ce86057..e9a21f802e 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -5,7 +5,7 @@ # - livenessProbe # - readinessProbe -"$schema": http://json-schema.org/schema# +"$schema": http://json-schema.org/draft/2019-09/schema type: object properties: fullnameOverride: From 90df806d4fb2ba52246db0a36bdb12d56917b4af Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 16:48:19 +0100 Subject: [PATCH 08/43] schema: make root objects required --- jupyterhub/schema.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index e9a21f802e..8b80ea86cb 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -7,6 +7,18 @@ "$schema": http://json-schema.org/draft/2019-09/schema type: object +required: + - imagePullSecrets + - hub + - proxy + - singleuser + - ingress + - prePuller + - custom + - cull + - debug + - rbac + - global properties: fullnameOverride: type: From cd66cf67744556fb138deccfb1f0bd5b1feb21a7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 16:55:19 +0100 Subject: [PATCH 09/43] schema: refactor type from yaml to json array --- jupyterhub/schema.yaml | 152 +++++++++++------------------------------ 1 file changed, 38 insertions(+), 114 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 8b80ea86cb..537a9fd661 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -21,9 +21,7 @@ required: - global properties: fullnameOverride: - type: - - string - - "null" + type: [string, "null"] description: | fullnameOverride and nameOverride allow you to adjust how the resources part of the Helm chart are named. @@ -69,9 +67,7 @@ properties: ``` nameOverride: - type: - - string - - "null" + type: [string, "null"] description: | See the documentation under [`fullnameOverride`](schema_fullnameOverride). @@ -119,9 +115,7 @@ properties: Toggle the automatic reference injection of the created Secret to all pods' `spec.imagePullSecrets` configuration. registry: - type: - - string - - "null" + type: [string, "null"] description: | Name of the private registry you want to create a credential set for. It will default to Docker Hub's image registry. @@ -132,9 +126,7 @@ properties: - eu.gcr.io - alexmorreale.privatereg.net username: - type: - - string - - "null" + type: [string, "null"] description: | Name of the user you want to use to connect to your private registry. @@ -145,9 +137,7 @@ properties: - alex@pfc.com - _json_key password: - type: - - string - - "null" + type: [string, "null"] description: | Password for the private image registry's user. @@ -376,9 +366,7 @@ properties: as is required to avoid replacing the content of the entire directory we mount in. baseUrl: - type: - - string - - "null" + type: [string, "null"] description: | This is the equivalent of c.JupyterHub.base_url, but it is also needed by the Helm chart in general. So, instead of setting @@ -441,9 +429,7 @@ properties: For more details, see the [Kubernetes documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes). cookieSecret: - type: - - string - - "null" + type: [string, "null"] description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -474,9 +460,7 @@ properties: Set custom image name, tag, pullPolicy, or pullSecrets for the pod. properties: name: - type: - - string - - "null" + type: [string, "null"] description: | The name of the image, without the tag. @@ -485,9 +469,7 @@ properties: gcr.io/my-project/my-image ``` tag: - type: - - string - - "null" + type: [string, "null"] description: | The tag of the image to pull. This is the value following `:` in complete image specifications. @@ -498,9 +480,7 @@ properties: zhy270a ``` pullPolicy: - type: - - string - - "null" + type: [string, "null"] enum: - "" - IfNotPresent @@ -561,9 +541,7 @@ properties: If you want to restrict egress, you can override this permissive default to be an empty list. interNamespaceAccessLabels: - type: - - string - - "null" + type: [string, "null"] enum: - accept - ignore @@ -595,9 +573,7 @@ properties: type: object properties: type: - type: - - string - - "null" + type: [string, "null"] enum: - sqlite-pvc - sqlite-memory @@ -689,9 +665,7 @@ properties: for more details about using a label selector for what PV to bind to. storage: - type: - - string - - "null" + type: [string, "null"] description: | Size of disk to request for the database disk. accessModes: # DESCRIBE ME @@ -705,17 +679,13 @@ properties: upgrade: # DESCRIBE ME type: "null" url: - type: - - string - - "null" + type: [string, "null"] description: | Connection string when `hub.db.type` is mysql or postgres. See documentation for `hub.db.type` for more details on the format of this property. password: - type: - - string - - "null" + type: [string, "null"] description: | Password for the database when `hub.db.type` is mysql or postgres. labels: @@ -841,9 +811,7 @@ properties: Object to configure the service the JupyterHub will be exposed on by the Kubernetes server. properties: type: - type: - - string - - "null" + type: [string, "null"] enum: - ClusterIP - NodePort @@ -861,9 +829,7 @@ properties: Object to configure the ports the hub service will be deployed on. properties: nodePort: - type: - - integer - - "null" + type: [integer, "null"] description: | The nodePort to deploy the hub service on. annotations: @@ -904,9 +870,7 @@ properties: The minimum number of pods required to be available during voluntary disruptions. existingSecret: - type: - - string - - "null" + type: [string, "null"] description: | Name of an existing k8s Secret to use instead of the chart managed k8s Secret. @@ -1104,9 +1068,7 @@ properties: resources: # DESCRIBE ME type: object secretToken: - type: - - string - - "null" + type: [string, "null"] description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -1137,9 +1099,7 @@ properties: the `proxy` pod and only accepting HTTP traffic on port 80. properties: type: - type: - - string - - "null" + type: [string, "null"] enum: - ClusterIP - NodePort @@ -1172,15 +1132,11 @@ properties: for more details about NodePorts. properties: http: - type: - - integer - - "null" + type: [integer, "null"] description: | The HTTP port the proxy-public service should be exposed on. https: - type: - - integer - - "null" + type: [integer, "null"] description: | The HTTPS port the proxy-public service should be exposed on. extraPorts: @@ -1194,9 +1150,7 @@ properties: documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core) for the structure of the items in this list. loadBalancerIP: - type: - - string - - "null" + type: [string, "null"] description: | The public IP address the proxy-public Kubernetes service should be exposed on. This entry will end up at the configurable proxy @@ -1223,9 +1177,7 @@ properties: description: | Indicator to set whether HTTPS should be enabled or not on the proxy. Defaults to `true` if the https object is provided. type: - type: - - string - - "null" + type: [string, "null"] enum: - letsencrypt - manual @@ -1239,9 +1191,7 @@ properties: type: object properties: contactEmail: - type: - - string - - "null" + type: [string, "null"] description: | The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. @@ -1254,9 +1204,7 @@ properties: See [Set up manual HTTPS](setup-manual-https) properties: key: - type: - - string - - "null" + type: [string, "null"] description: | The RSA private key to be used for HTTPS. To be provided in the form of @@ -1268,9 +1216,7 @@ properties: -----END RSA PRIVATE KEY----- ``` cert: - type: - - string - - "null" + type: [string, "null"] description: | The certificate to be used for HTTPS. To be provided in the form of @@ -1287,22 +1233,16 @@ properties: Secret to be provided when setting `https.type` to `secret`. properties: name: - type: - - string - - "null" + type: [string, "null"] description: | Name of the secret key: - type: - - string - - "null" + type: [string, "null"] description: | Path to the private key to be used for HTTPS. Example: `'tls.key'` crt: - type: - - string - - "null" + type: [string, "null"] description: | Path to the certificate to be used for HTTPS. Example: `'tls.crt'` @@ -1421,9 +1361,7 @@ properties: properties: networkPolicy: *networkPolicy-spec podNameTemplate: - type: - - string - - "null" + type: [string, "null"] description: | Template for the pod name of each user, such as `jupyter-{username}{servername}`. cpu: @@ -1433,13 +1371,9 @@ properties: See: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ properties: limit: - type: - - string - - "null" + type: [string, "null"] guarantee: - type: - - string - - "null" + type: [string, "null"] memory: type: object description: | @@ -1449,13 +1383,9 @@ properties: for more info. properties: limit: - type: - - string - - "null" + type: [string, "null"] guarantee: - type: - - string - - "null" + type: [string, "null"] description: | Note that this field is referred to as *requests* by the Kubernetes API. image: *image-spec @@ -1834,9 +1764,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: - - string - - "null" + type: [string, "null"] enum: - ignore - prefer @@ -1860,9 +1788,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: - - string - - "null" + type: [string, "null"] enum: - ignore - prefer @@ -1898,9 +1824,7 @@ properties: description: | List of hosts to route requests to the proxy. pathSuffix: - type: - - string - - "null" + type: [string, "null"] description: | Suffix added to Ingress's routing path pattern. From e90a185c379da737bf4840664e70221faa28372a Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 16:56:26 +0100 Subject: [PATCH 10/43] schema: add resources anchor --- jupyterhub/schema.yaml | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 537a9fd661..94302c3373 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,5 +1,4 @@ # Anchor candidates -# - resources # - image # - containerSecurityContext # - livenessProbe @@ -967,16 +966,11 @@ properties: type: integer redirectToServer: # DESCRIBE ME type: "null" - resources: # DESCRIBE ME + resources: &resources-spec type: object - properties: - requests: - type: object - properties: - cpu: - type: string - memory: - type: string + description: | + A k8s native specification of resources, see [the + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core). services: # DESCRIBE ME type: object shutdownOnLogout: # DESCRIBE ME @@ -1065,8 +1059,7 @@ properties: type: object readinessProbe: # DESCRIBE ME type: object - resources: # DESCRIBE ME - type: object + resources: *resources-spec secretToken: type: [string, "null"] description: | @@ -1331,8 +1324,7 @@ properties: type: boolean image: # DESCRIBE ME type: object - resources: # DESCRIBE ME - type: object + resources: *resources-spec labels: # DESCRIBE ME type: object annotations: # DESCRIBE ME @@ -1351,8 +1343,7 @@ properties: type: object image: type: object - resources: - type: object + resources: *resources-spec singleuser: type: object @@ -1659,8 +1650,7 @@ properties: type: string weight: type: integer - resources: # DESCRIBE ME - type: object + resources: *resources-spec podPriority: type: object description: | @@ -1898,8 +1888,7 @@ properties: type: array tag: type: string - resources: # DESCRIBE ME - type: object + resources: *resources-spec continuous: description: | See the [*optimization From 4c26b0785d8da274271b15ed4814c46faf286c2d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 17:00:12 +0100 Subject: [PATCH 11/43] schema: add image anchor references --- jupyterhub/schema.yaml | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 94302c3373..114b284448 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,5 +1,4 @@ # Anchor candidates -# - image # - containerSecurityContext # - livenessProbe # - readinessProbe @@ -1053,8 +1052,7 @@ properties: tolerations: *tolerations-spec containerSecurityContext: # DESCRIBE ME type: object - image: # DESCRIBE ME - type: object + image: *image-spec livenessProbe: # DESCRIBE ME type: object readinessProbe: # DESCRIBE ME @@ -1322,8 +1320,7 @@ properties: type: integer preload: type: boolean - image: # DESCRIBE ME - type: object + image: *image-spec resources: *resources-spec labels: # DESCRIBE ME type: object @@ -1341,8 +1338,7 @@ properties: properties: containerSecurityContext: type: object - image: - type: object + image: *image-spec resources: *resources-spec singleuser: @@ -1555,8 +1551,7 @@ properties: networkTools: # DESCRIBE ME type: object properties: - image: - type: object + image: *image-spec serviceAccountName: # DESCRIBE ME type: "null" startTimeout: # DESCRIBE ME @@ -1877,17 +1872,7 @@ properties: tolerations: *tolerations-spec containerSecurityContext: # DESCRIBE ME type: object - image: # DESCRIBE ME - type: object - properties: - name: - type: string - pullPolicy: - type: string - pullSecrets: - type: array - tag: - type: string + image: *image-spec resources: *resources-spec continuous: description: | @@ -1933,17 +1918,7 @@ properties: properties: containerSecurityContext: type: object - image: - type: object - properties: - name: - type: string - pullPolicy: - type: string - pullSecrets: - type: array - tag: - type: string + image: *image-spec custom: type: object From 49a11ddd0513ab508fa440bd3859960271879543 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 17:10:16 +0100 Subject: [PATCH 12/43] schema: add containerSecurityContext anchors --- jupyterhub/schema.yaml | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 114b284448..fc610469c5 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,5 +1,4 @@ # Anchor candidates -# - containerSecurityContext # - livenessProbe # - readinessProbe @@ -920,8 +919,12 @@ properties: type: integer consecutiveFailureLimit: # DESCRIBE ME type: integer - containerSecurityContext: # DESCRIBE ME + containerSecurityContext: &containerSecurityContext-spec type: object + 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) + for details. deploymentStrategy: # DESCRIBE ME type: object properties: @@ -1050,8 +1053,7 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec image: *image-spec livenessProbe: # DESCRIBE ME type: object @@ -1299,8 +1301,7 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec extraDynamicConfig: # DESCRIBE ME type: object extraPorts: # DESCRIBE ME @@ -1336,8 +1337,7 @@ properties: secretSync: # DESCRIBE ME type: object properties: - containerSecurityContext: - type: object + containerSecurityContext: *containerSecurityContext-spec image: *image-spec resources: *resources-spec @@ -1619,8 +1619,7 @@ properties: pdb: *pdb-spec nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec logLevel: # DESCRIBE ME type: integer plugins: # DESCRIBE ME @@ -1734,8 +1733,7 @@ properties: description: | Unless specified here, the placeholder pods will request the same resources specified for the real singleuser pods. - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec corePods: type: object description: | @@ -1870,8 +1868,7 @@ properties: type: integer nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec image: *image-spec resources: *resources-spec continuous: @@ -1911,13 +1908,11 @@ properties: name: jupyter/all-spark-notebook tag: 2343e33dec46 ``` - containerSecurityContext: # DESCRIBE ME - type: object + containerSecurityContext: *containerSecurityContext-spec pause: # DESCRIBE ME type: object properties: - containerSecurityContext: - type: object + containerSecurityContext: *containerSecurityContext-spec image: *image-spec custom: From 599048871daea8b87a339b82a4be36d4b1871d6d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 17:20:53 +0100 Subject: [PATCH 13/43] schema: add liveness-/readinessProbe anchors --- jupyterhub/schema.yaml | 45 +++++++++++------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fc610469c5..9362706593 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,7 +1,3 @@ -# Anchor candidates -# - livenessProbe -# - readinessProbe - "$schema": http://json-schema.org/draft/2019-09/schema type: object required: @@ -938,34 +934,19 @@ properties: type: array extraVolumes: # DESCRIBE ME type: array - livenessProbe: # DESCRIBE ME + livenessProbe: &probe-spec type: object - properties: - enabled: - type: boolean - failureThreshold: - type: integer - initialDelaySeconds: - type: integer - periodSeconds: - type: integer - timeoutSeconds: - type: integer + description: | + This config option is exactly like the k8s native specification of a + container probe, except that it also supports an `enabled` boolean + flag. + + See [the k8s + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#probe-v1-core) + for more details. + readinessProbe: *probe-spec namedServerLimitPerUser: # DESCRIBE ME type: "null" - readinessProbe: # DESCRIBE ME - type: object - properties: - enabled: - type: boolean - failureThreshold: - type: integer - initialDelaySeconds: - type: integer - periodSeconds: - type: integer - timeoutSeconds: - type: integer redirectToServer: # DESCRIBE ME type: "null" resources: &resources-spec @@ -1055,10 +1036,8 @@ properties: tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec image: *image-spec - livenessProbe: # DESCRIBE ME - type: object - readinessProbe: # DESCRIBE ME - type: object + livenessProbe: *probe-spec + readinessProbe: *probe-spec resources: *resources-spec secretToken: type: [string, "null"] From 303a3f992b76d291160411b84f87eb4341b33434 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 17:32:37 +0100 Subject: [PATCH 14/43] schema: small details iteration --- jupyterhub/schema.yaml | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 9362706593..a2bb6ad714 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -153,8 +153,11 @@ properties: Learn more in [this guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). - email: # DESCRIBE ME - type: string + email: + type: [string, "null"] + description: | + Specification of an email is most often not required, but it is + supported. imagePullSecrets: type: array @@ -666,11 +669,11 @@ properties: items: type: string storageClassName: # DESCRIBE ME - type: "null" + type: [string, null"] subPath: # DESCRIBE ME - type: "null" + type: [string, "null"] upgrade: # DESCRIBE ME - type: "null" + type: [boolean, "null"] url: type: [string, "null"] description: | @@ -904,13 +907,13 @@ properties: docs](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more info. activeServerLimit: # DESCRIBE ME - type: "null" + type: [integer, "null"] allowNamedServers: # DESCRIBE ME type: boolean annotations: # DESCRIBE ME type: object authenticatePrometheus: # DESCRIBE ME - type: "null" + type: [boolean, "null"] concurrentSpawnLimit: # DESCRIBE ME type: integer consecutiveFailureLimit: # DESCRIBE ME @@ -928,6 +931,7 @@ properties: type: string extraConfigMap: # DESCRIBE ME type: object + deprecated: true extraContainers: # DESCRIBE ME type: array extraVolumeMounts: # DESCRIBE ME @@ -946,9 +950,9 @@ properties: for more details. readinessProbe: *probe-spec namedServerLimitPerUser: # DESCRIBE ME - type: "null" + type: [integer, "null"] redirectToServer: # DESCRIBE ME - type: "null" + type: [boolean, "null"] resources: &resources-spec type: object description: | @@ -957,7 +961,7 @@ properties: services: # DESCRIBE ME type: object shutdownOnLogout: # DESCRIBE ME - type: "null" + type: [boolean, "null"] templatePaths: # DESCRIBE ME type: array templateVars: # DESCRIBE ME @@ -1167,8 +1171,14 @@ properties: description: | The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. - acmeServer: # DESCRIBE ME + acmeServer: type: string + description: | + Let's Encrypt is one of various ACME servers that can provide + a certificate, and by default their production server is used. + + Let's Encrypt staging: https://acme-staging-v02.api.letsencrypt.org/directory + Let's Encrypt production: acmeServer: https://acme-v02.api.letsencrypt.org/directory manual: type: object description: | From 4c7e9e0846a62dfc0075e4e8591d1daf5d89cf5c Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 18:48:34 +0100 Subject: [PATCH 15/43] schema: worked through several describe me fields --- jupyterhub/schema.yaml | 104 ++++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 32 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index a2bb6ad714..280720f406 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -664,16 +664,30 @@ properties: type: [string, "null"] description: | Size of disk to request for the database disk. - accessModes: # DESCRIBE ME + accessModes: type: array items: type: string - storageClassName: # DESCRIBE ME - type: [string, null"] - subPath: # DESCRIBE ME + description: | + AccessModes contains the desired access modes the volume + should have. See [the k8s + documentation](https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1) + for more information. + storageClassName: type: [string, "null"] - upgrade: # DESCRIBE ME + description: | + Name of the StorageClass required by the claim. + subPath: + type: [string, "null"] + description: | + Path within the volume from which the container's volume + should be mounted. Defaults to "" (volume's root). + upgrade: type: [boolean, "null"] + description: | + Users with external databases need to opt-in for upgrades of the + JupyterHub specific database schema if needed as part of a + JupyterHub version upgrade. url: type: [string, "null"] description: | @@ -906,38 +920,58 @@ properties: See the [Kubernetes docs](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) for more info. - activeServerLimit: # DESCRIBE ME + activeServerLimit: type: [integer, "null"] - allowNamedServers: # DESCRIBE ME + description: &jupyterhub-native-config-description | + JupyterHub native configuration, see the [JupyterHub + documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html) + for more information. + allowNamedServers: type: boolean + description: *jupyterhub-native-config-description annotations: # DESCRIBE ME type: object - authenticatePrometheus: # DESCRIBE ME + authenticatePrometheus: type: [boolean, "null"] - concurrentSpawnLimit: # DESCRIBE ME + description: *jupyterhub-native-config-description + concurrentSpawnLimit: type: integer - consecutiveFailureLimit: # DESCRIBE ME + description: *jupyterhub-native-config-description + consecutiveFailureLimit: type: integer + description: *jupyterhub-native-config-description containerSecurityContext: &containerSecurityContext-spec type: object 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) for details. - deploymentStrategy: # DESCRIBE ME + deploymentStrategy: type: object properties: type: type: string - extraConfigMap: # DESCRIBE ME + description: | + JupyterHub does not support running in parallel, due to this we + default to using a deployment strategy of Recreate. + extraConfigMap: type: object deprecated: true - extraContainers: # DESCRIBE ME + description: | + Deprecated since chart version 0.8. Use [`custom`](schema_custom) + instead. + extraContainers: &extraContainers-spec type: array - extraVolumeMounts: # DESCRIBE ME + description: | + Additional containers for the Pod. Use a k8s native syntax. + extraVolumeMounts: &extraVolumeMounts-spec type: array - extraVolumes: # DESCRIBE ME + description: | + Additional volume mounts for the Container. Use a k8s native syntax. + extraVolumes: &extraVolumes-spec type: array + description: | + Additional volumes for the Pod. Use a k8s native syntax. livenessProbe: &probe-spec type: object description: | @@ -949,23 +983,36 @@ properties: documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#probe-v1-core) for more details. readinessProbe: *probe-spec - namedServerLimitPerUser: # DESCRIBE ME + namedServerLimitPerUser: type: [integer, "null"] - redirectToServer: # DESCRIBE ME + description: *jupyterhub-native-config-description + redirectToServer: type: [boolean, "null"] + description: *jupyterhub-native-config-description resources: &resources-spec type: object description: | A k8s native specification of resources, see [the documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core). - services: # DESCRIBE ME + services: type: object - shutdownOnLogout: # DESCRIBE ME + description: | + You can refer to the [JupyterHub + documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html) + except that you need to pass the services as a dictionary instead of a + list. + + The `name` field can be filled in by the dictionary keys, and a + camelCase formatting of api_token is also accepted. + shutdownOnLogout: type: [boolean, "null"] - templatePaths: # DESCRIBE ME + description: *jupyterhub-native-config-description + templatePaths: type: array - templateVars: # DESCRIBE ME + description: *jupyterhub-native-config-description + templateVars: type: object + description: *jupyterhub-native-config-description proxy: type: object @@ -1297,12 +1344,8 @@ properties: type: array extraStaticConfig: # DESCRIBE ME type: object - extraVolumeMounts: # DESCRIBE ME - type: array - extraVolumes: # DESCRIBE ME - type: array + extraVolumeMounts: *extraVolumeMounts-spec hsts: # DESCRIBE ME - type: object properties: includeSubdomains: type: boolean @@ -1517,8 +1560,7 @@ properties: type: boolean extraAnnotations: # DESCRIBE ME type: object - extraContainers: # DESCRIBE ME - type: array + extraContainers: *extraContainers-spec extraLabels: # DESCRIBE ME type: object properties: @@ -1565,10 +1607,8 @@ properties: type: string extraLabels: type: object - extraVolumeMounts: - type: array - extraVolumes: - type: array + extraVolumeMounts: *extraVolumeMounts-spec + extraVolumes: *extraVolumes-spec homeMountPath: type: string static: From bbe8c136a6eb78a23a4204f5f3e8357586cb03f8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 20:18:12 +0100 Subject: [PATCH 16/43] schema: add cull entry --- jupyterhub/schema.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 280720f406..3e5c35a45b 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1961,23 +1961,35 @@ properties: c.MyAuthenticator.host = get_config("custom.myHost") ``` - cull: # DESCRIBE ME + cull: type: object + description: | + The + [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) + can run as a JupyterHub managed service to _cull_ running servers. properties: enabled: type: boolean + description: | + Enable/disable use of jupyter-idle-culler. users: type: boolean + description: See the `--cull-users` flag. removeNamedServers: type: boolean + description: See the `--remove-named-servers` flag. timeout: type: integer + description: See the `--timeout` flag. every: type: integer + description: See the `--cull-every` flag. concurrency: type: integer + description: See the `--concurrency` flag. maxAge: type: integer + description: See the `--max-age` flag. debug: type: object From ca88b75ab45a55531dc7b28661691c6dafea32da Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 20:22:03 +0100 Subject: [PATCH 17/43] schema: document global --- jupyterhub/schema.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 3e5c35a45b..c245ce1ff7 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1999,14 +1999,21 @@ properties: description: | Increases the loglevel throughout the resources in the Helm chart. - rbac: # DESCRIBE ME + rbac: type: object properties: enabled: type: boolean + description: | + Decides if RBAC resources are to be created and referenced by the the + Helm chart's workloads. - global: # DESCRIBE ME + global: type: object properties: safeToShowValues: type: boolean + description: | + A flag that should only be set to true temporarily when experiencing a + deprecation message that contain censored content that you wish to + reveal. From 864b32e0f5485d2b9e5c0d64da9b0b2e0adea0e9 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 20:23:49 +0100 Subject: [PATCH 18/43] schema: image-puller's pause container --- jupyterhub/schema.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index c245ce1ff7..0fa2450a17 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1938,8 +1938,12 @@ properties: tag: 2343e33dec46 ``` containerSecurityContext: *containerSecurityContext-spec - pause: # DESCRIBE ME + pause: type: object + description: | + The image-puller pods rely on initContainer to pull all images, and + their actual container when they are done is just running a `pause` + container. These are settings for that pause container. properties: containerSecurityContext: *containerSecurityContext-spec image: *image-spec From 3e8cbf305be83fdc8b268a8f769b0229bf066742 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sat, 13 Feb 2021 22:26:17 +0100 Subject: [PATCH 19/43] schema: all but singleuser.storage remain to describe --- jupyterhub/schema.yaml | 182 +++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 35 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 0fa2450a17..ff772304ce 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -929,8 +929,10 @@ properties: allowNamedServers: type: boolean description: *jupyterhub-native-config-description - annotations: # DESCRIBE ME + annotations: type: object + description: | + K8s annotations for the hub pod. authenticatePrometheus: type: [boolean, "null"] description: *jupyterhub-native-config-description @@ -1338,14 +1340,46 @@ properties: nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec - extraDynamicConfig: # DESCRIBE ME + extraDynamicConfig: type: object - extraPorts: # DESCRIBE ME + description: | + This refers to traefik's post-startup configuration. + + This Helm chart already provide such configuration, so this is a + place where you can merge in additional configuration. If you are + about to use this configuration, you may want to inspect the + default configuration declared + [here](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/master/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml). + extraPorts: type: array - extraStaticConfig: # DESCRIBE ME + description: | + Extra ports for the traefik container within the autohttps pod + that you would like to expose, formatted in a k8s native way. + extraStaticConfig: type: object + description: | + This refers to traefik's startup configuration. + + This Helm chart already provide such configuration, so this is a + place where you can merge in additional configuration. If you are + about to use this configuration, you may want to inspect the + default configuration declared + [here](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/master/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml). extraVolumeMounts: *extraVolumeMounts-spec - hsts: # DESCRIBE ME + hsts: + type: object + description: | + This section regards a HTTP Strict-Transport-Security (HSTS) + response header. It can act as a request for a visiting web + browsers to enforce HTTPS on their end in for a given time into + the future, and optionally also for future requests to subdomains. + + These settings relate to traefik configuration which we use as a + TLS termination proxy. + + See [Mozilla's + documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) + for more information. properties: includeSubdomains: type: boolean @@ -1355,19 +1389,49 @@ properties: type: boolean image: *image-spec resources: *resources-spec - labels: # DESCRIBE ME + labels: type: object - annotations: # DESCRIBE ME + description: | + K8s labels for the proxy pod. + + ```{note} + For consistency, this should really be located under + proxy.chp.labels but isn't for historical reasons. + ``` + annotations: type: object - deploymentStrategy: # DESCRIBE ME + description: | + K8s annotations for the proxy pod. + + ```{note} + For consistency, this should really be located under + proxy.chp.annotations but isn't for historical reasons. + ``` + deploymentStrategy: type: object properties: rollingUpdate: - type: "null" + type: [string, "null"] type: type: string - secretSync: # DESCRIBE ME + description: | + While the proxy pod running + [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) + could run in parallel, two instances running in parallel wouldn't + 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. + secretSync: type: object + description: | + This configuration section refers to configuration of the sidecar + container in the autohttps pod running next to its traefik container + responsible for TLS termination. + + The purpose of this container is to store away and load TLS + certificates from a k8s Secret. The TLS certificates are acquired by + the ACME client (LEGO) that is running within the traefik container, + where traefik is using them for TLS termination. properties: containerSecurityContext: *containerSecurityContext-spec image: *image-spec @@ -1545,48 +1609,79 @@ properties: Pass this field an array of [`WeightedPodAffinityTerm`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#weightedpodaffinityterm-v1-core) objects. - cloudMetadata: # DESCRIBE ME + cloudMetadata: type: object + description: | + Please refer to dedicated section in [the Helm chart + documentation](block-metadata-iptables) for more information about + this. properties: blockWithIptables: type: boolean ip: type: string - cmd: # DESCRIBE ME - type: string - defaultUrl: # DESCRIBE ME - type: "null" - events: # DESCRIBE ME + cmd: + type: [array, string] + description: &kubespawner-native-config-description | + KubeSpawner native configuration, see the [KubeSpawner + documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) + for more information. + defaultUrl: + type: [string, "null"] + description: *kubespawner-native-config-description + # FIXME: named events_enabled in kubespawner + events: type: boolean - extraAnnotations: # DESCRIBE ME + description: *kubespawner-native-config-description + extraAnnotations: type: object - extraContainers: *extraContainers-spec - extraLabels: # DESCRIBE ME + description: *kubespawner-native-config-description + extraContainers: + type: array + description: *kubespawner-native-config-description + extraLabels: type: object - properties: - hub.jupyter.org/network-access-hub: - type: string - extraPodConfig: # DESCRIBE ME + description: *kubespawner-native-config-description + extraPodConfig: type: object - extraResource: # DESCRIBE ME + description: *kubespawner-native-config-description + extraResource: type: object properties: + # FIXME: named extra_resource_guarantees in kubespawner guarantees: type: object + description: *kubespawner-native-config-description + # FIXME: named extra_resource_limits in kubespawner limits: type: object - fsGid: # DESCRIBE ME - type: integer - lifecycleHooks: # DESCRIBE ME + description: *kubespawner-native-config-description + fsGid: + type: [integer, "null"] + description: *kubespawner-native-config-description + lifecycleHooks: type: object - networkTools: # DESCRIBE ME + description: *kubespawner-native-config-description + networkTools: type: object + description: | + This configuration section refers to configuration of a conditionally + created initContainer for the user pods with a purpose to block a + specific IP address. + + This initContainer will be created if + [`singleuser.cloudMetadata.blockWithIptables`](schema_singleuser.cloudMetadata.blockWithIptables) + is set to true. properties: image: *image-spec - serviceAccountName: # DESCRIBE ME - type: "null" - startTimeout: # DESCRIBE ME + # FIXME: no resources here + # FIXME: named service_account in kubespawner + serviceAccountName: + type: [string, "null"] + description: *kubespawner-native-config-description + startTimeout: type: integer + description: *kubespawner-native-config-description storage: # DESCRIBE ME type: object properties: @@ -1620,8 +1715,15 @@ properties: type: string type: type: string - uid: # DESCRIBE ME + uid: type: integer + description: | + 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 + 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. scheduling: type: object @@ -1649,10 +1751,20 @@ properties: nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec - logLevel: # DESCRIBE ME + logLevel: type: integer - plugins: # DESCRIBE ME + description: | + Corresponds to the verbosity level of logging made by the + kube-scheduler binary running within the user-scheduler pod. + plugins: type: object + description: | + These plugins refers to kube-scheduler plugins as documented + [here](https://kubernetes.io/docs/reference/scheduling/config/). + + 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 @@ -1826,7 +1938,7 @@ properties: annotations: type: object description: | - Annotations to apply to the Ingress. + Annotations to apply to the Ingress resource. See [the Kubernetes documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) From a1a099083274e3c739fdb45ec9b25a9a59f89cb6 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 01:15:22 +0100 Subject: [PATCH 20/43] schema: initial full manual passthrough of values/schema 1:1 mapping --- doc/source/administrator/security.md | 4 ++ jupyterhub/schema.yaml | 69 ++++++++++++++++--- .../scheduling/user-scheduler/deployment.yaml | 2 +- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/doc/source/administrator/security.md b/doc/source/administrator/security.md index de929ba7b0..e813308778 100644 --- a/doc/source/administrator/security.md +++ b/doc/source/administrator/security.md @@ -255,6 +255,8 @@ provides more information on the dangers presented by this attack. This Helm chart blocks access to this metadata in two ways by default, but you only need one. +(block-metadata-netpol)= + ### Block metadata with a NetworkPolicy enforced by a NetworkPolicy controller If you have _NetworkPolicy controller_ such as Calico in the Kubernetes cluster, @@ -263,6 +265,8 @@ it will enforce the NetworkPolicy resource created by this chart We recommend relying on this approach if you you had a NetworkPolicy controller, and then you can disable the other option. +(block-metadata-iptables)= + ### Block metadata with a privileged initContainer running `iptables` If you can't rely on the NetworkPolicy approach to block access to the metadata diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index ff772304ce..76f5df06af 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1629,7 +1629,7 @@ properties: defaultUrl: type: [string, "null"] description: *kubespawner-native-config-description - # FIXME: named events_enabled in kubespawner + # FIXME: name mismatch, named events_enabled in kubespawner events: type: boolean description: *kubespawner-native-config-description @@ -1648,11 +1648,11 @@ properties: extraResource: type: object properties: - # FIXME: named extra_resource_guarantees in kubespawner + # FIXME: name mismatch, named extra_resource_guarantees in kubespawner guarantees: type: object description: *kubespawner-native-config-description - # FIXME: named extra_resource_limits in kubespawner + # FIXME: name mismatch, named extra_resource_limits in kubespawner limits: type: object description: *kubespawner-native-config-description @@ -1675,46 +1675,97 @@ properties: properties: image: *image-spec # FIXME: no resources here - # FIXME: named service_account in kubespawner + # FIXME: name mismatch, named service_account in kubespawner serviceAccountName: type: [string, "null"] description: *kubespawner-native-config-description startTimeout: type: integer description: *kubespawner-native-config-description - storage: # DESCRIBE ME + storage: type: object + description: | + This section configures KubeSpawner directly to some extent but also + indirectly through Helm chart specific configuration options such as + [`singleuser.storage.type`](schema_singleuser.storage.type). properties: capacity: type: string + description: | + Configures `KubeSpawner.storage_capacity`. + + See the [KubeSpawner + documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) + for more information. dynamic: type: object properties: pvcNameTemplate: type: string + description: | + Configures `KubeSpawner.pvc_name_template` which will be the + resource name of the PVC created by KubeSpawner for each user + if needed. storageAccessModes: type: array items: type: string + description: | + Configures `KubeSpawner.storage_access_modes`. + + See KubeSpawners documentation and [the k8s + documentation](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) + for more information. storageClass: - type: "null" + type: [string, "null"] + description: | + Configures `KubeSpawner.storage_class`, which can be an + explicit StorageClass to dynamically provision storage for the + PVC that KubeSpawner will create. + + There is of a default StorageClass available in k8s clusters + for use if this is unspecified. volumeNameTemplate: type: string + description: | + Configures `KubeSpawner.volume_name_template`, which is the + name to reference from the containers volumeMounts section. extraLabels: type: object + description: | + Configures `KubeSpawner.storage_extra_labels`. Note that these + labels are set on the PVC during creation only and won't be + updated after creation. extraVolumeMounts: *extraVolumeMounts-spec extraVolumes: *extraVolumes-spec homeMountPath: type: string + description: | + The location within the container where the home folder storage + should be mounted. static: type: object properties: pvcName: - type: "null" + type: [string, "null"] + description: | + Configures `KubeSpawner.pvc_claim_name` to reference + pre-existing storage. subPath: - type: string + type: [string, "null"] + description: | + Configures the `subPath` field of a + `KubeSpawner.volume_mounts` entry added by the Helm chart. + + Path within the volume from which the container's volume + should be mounted. type: - type: string + type: [string, "null"] + enum: [dynamic, static, none] + description: | + 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). uid: type: integer description: | diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml index ab76fd06ef..9e880f53c8 100644 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -66,7 +66,7 @@ spec: # ref: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/1894 - --config=/etc/user-scheduler/config.yaml - --authentication-skip-lookup=true - - --v={{ .Values.scheduling.userScheduler.logLevel | default 4 }} + - --v={{ .Values.scheduling.userScheduler.logLevel }} volumeMounts: - mountPath: /etc/user-scheduler name: config From 97e8c50b700583ef4c9eb51fba00b76909e28792 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 01:22:11 +0100 Subject: [PATCH 21/43] docs: fix myst syntax about notes --- doc/source/administrator/advanced.md | 2 +- jupyterhub/schema.yaml | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/source/administrator/advanced.md b/doc/source/administrator/advanced.md index fcee902e51..12f69f18c4 100644 --- a/doc/source/administrator/advanced.md +++ b/doc/source/administrator/advanced.md @@ -19,7 +19,7 @@ resource](https://kubernetes.io/docs/concepts/services-networking/ingress/) to expose JupyterHub using an [Ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/). -```note +```{note} Not all k8s clusters are setup with an Ingress controller by default. If you need to install one manually, we recommend using [ingress-nginx](https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md#using-helm). diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 76f5df06af..4e64fc0a01 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1494,9 +1494,11 @@ properties: documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html#kubespawner.KubeSpawner) as this is simply a passthrough to that configuration. - **NOTE**: The image-pullers are aware of the overrides of images in + ```{note} + The image-pullers are aware of the overrides of images in `singleuser.profileList` but they won't be if you configure it in JupyterHub's configuration of '`c.KubeSpawner.profile_list`. + ``` ```yaml singleuser: @@ -2069,8 +2071,10 @@ properties: section*](pulling-images-before-users-arrive) for more details. - **NOTE**: If used with a Cluster Autoscaler (an autoscaling node - pool), also add user-placeholders and enable pod priority. + ```{note} + If used with a Cluster Autoscaler (an autoscaling node pool), also add + user-placeholders and enable pod priority. + ``` type: object properties: enabled: From c39f7af1789c9485d779d9eb305bc5dbad0c0292 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 04:39:10 +0100 Subject: [PATCH 22/43] schema: last fixes following script validation --- jupyterhub/schema.yaml | 8 +++ jupyterhub/validate.py | 65 ++++++++++++++++++- jupyterhub/values.yaml | 4 +- tools/templates/lint-and-validate-values.yaml | 5 ++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 4e64fc0a01..fc6ab0488e 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -846,6 +846,13 @@ properties: type: object description: | Kubernetes annotations to apply to the hub service. + loadBalancerIP: + type: [string, "null"] + description: | + A public IP address the hub Kubernetes service should be exposed + on. To expose the hub directly is not recommended. Instead route + traffic through the proxy-public service towards the hub. + pdb: &pdb-spec type: object description: | @@ -1365,6 +1372,7 @@ properties: about to use this configuration, you may want to inspect the default configuration declared [here](https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/master/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml). + extraVolumes: *extraVolumes-spec extraVolumeMounts: *extraVolumeMounts-spec hsts: type: object diff --git a/jupyterhub/validate.py b/jupyterhub/validate.py index fea845c21a..335d554c1f 100755 --- a/jupyterhub/validate.py +++ b/jupyterhub/validate.py @@ -1,9 +1,68 @@ #!/usr/bin/env python3 +from collections.abc import MutableMapping import jsonschema import yaml -# HACK: These files are never closed, but is ok! -schema = yaml.safe_load(open("schema.yaml")) -values = yaml.safe_load(open("values.yaml")) +with open("schema.yaml") as f: + schema = yaml.safe_load(f) +# Validate values.yaml against schema +print("Validating values.yaml...") +with open("values.yaml") as f: + values = yaml.safe_load(f) jsonschema.validate(values, schema) +print("OK!") +print() + +# Validate lint-and-validate-values.yaml against schema +print("Validating lint-and-validate-values.yaml...") +with open("../tools/templates/lint-and-validate-values.yaml") as f: + lint_and_validate_values = yaml.safe_load(f) +jsonschema.validate(lint_and_validate_values, schema) +print("OK!") + + +def reduce_schema(d): + """ + Takes a jsonschema loaded as a dictionary and return a reduced structure + ignoring everything apart from the structure it describes. + """ + 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 + return r + + +def flatten(d, parent_key="", sep="."): + """ + Takes a nested dictionary and return all keys flattened using a separator, + so one element returned would for example be "hub.image.tag". + """ + items = [] + for k, v in d.items(): + new_key = parent_key + sep + k if parent_key else k + if isinstance(v, MutableMapping): + if v: + 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 + + +# Using these sets, you can validate further manually by printing the results of +# set operations. +schema_keys = flatten(reduce_schema(schema)) +values_keys = flatten(values) +lint_and_validate_values_keys = flatten(lint_and_validate_values) + +# print(values_keys - schema_keys) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 761639e570..5a9a928d65 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -239,6 +239,7 @@ proxy: preload: false maxAge: 15724800 # About 6 months resources: {} + labels: {} extraEnv: {} extraVolumes: [] extraVolumeMounts: [] @@ -517,4 +518,5 @@ cull: debug: enabled: false -global: {} +global: + safeToShowValues: false diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index 5d2903acb4..f31442ca66 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -456,3 +456,8 @@ cull: debug: enabled: true + +global: {} + +custom: + myCustomStuff: [hello] From 15a61ecf2b1a08c45b920b9a3b1d27629e2d9874 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 20:01:29 +0100 Subject: [PATCH 23/43] Relocate validate schema script to tools --- jupyterhub/.helmignore | 1 - jupyterhub/validate.py => tools/validate-against-schema.py | 0 2 files changed, 1 deletion(-) rename jupyterhub/validate.py => tools/validate-against-schema.py (100%) diff --git a/jupyterhub/.helmignore b/jupyterhub/.helmignore index 7eded7e32a..05f3c9858d 100644 --- a/jupyterhub/.helmignore +++ b/jupyterhub/.helmignore @@ -7,7 +7,6 @@ # 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 -validate.py # Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and diff --git a/jupyterhub/validate.py b/tools/validate-against-schema.py similarity index 100% rename from jupyterhub/validate.py rename to tools/validate-against-schema.py From f51dd2e6d3452f8dbfced73c132c6af49f1a03dc Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 20:05:16 +0100 Subject: [PATCH 24/43] Remove misplaced logic from schema validation script --- tools/validate-against-schema.py | 51 ++------------------------------ 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index 335d554c1f..52a1f7fec7 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from collections.abc import MutableMapping import jsonschema import yaml @@ -7,7 +6,7 @@ schema = yaml.safe_load(f) # Validate values.yaml against schema -print("Validating values.yaml...") +print("Validating values.yaml against schema.yaml...") with open("values.yaml") as f: values = yaml.safe_load(f) jsonschema.validate(values, schema) @@ -15,54 +14,8 @@ print() # Validate lint-and-validate-values.yaml against schema -print("Validating lint-and-validate-values.yaml...") +print("Validating lint-and-validate-values.yaml against schema.yaml...") with open("../tools/templates/lint-and-validate-values.yaml") as f: lint_and_validate_values = yaml.safe_load(f) jsonschema.validate(lint_and_validate_values, schema) print("OK!") - - -def reduce_schema(d): - """ - Takes a jsonschema loaded as a dictionary and return a reduced structure - ignoring everything apart from the structure it describes. - """ - 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 - return r - - -def flatten(d, parent_key="", sep="."): - """ - Takes a nested dictionary and return all keys flattened using a separator, - so one element returned would for example be "hub.image.tag". - """ - items = [] - for k, v in d.items(): - new_key = parent_key + sep + k if parent_key else k - if isinstance(v, MutableMapping): - if v: - 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 - - -# Using these sets, you can validate further manually by printing the results of -# set operations. -schema_keys = flatten(reduce_schema(schema)) -values_keys = flatten(values) -lint_and_validate_values_keys = flatten(lint_and_validate_values) - -# print(values_keys - schema_keys) From 765037911220db1813c189406eee230d8d1154af Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 20:10:26 +0100 Subject: [PATCH 25/43] schema-validation-script: refactor for tools folder --- tools/validate-against-schema.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index 52a1f7fec7..c48d2782d4 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -1,21 +1,26 @@ #!/usr/bin/env python3 import jsonschema +import os +import sys import yaml -with open("schema.yaml") as f: +# Change current directory to this directory +os.chdir(os.path.dirname(sys.argv[0])) + +with open("../jupyterhub/schema.yaml") as f: schema = yaml.safe_load(f) +with open("../jupyterhub/values.yaml") as f: + values = yaml.safe_load(f) +with open("templates/lint-and-validate-values.yaml") as f: + lint_and_validate_values = yaml.safe_load(f) # Validate values.yaml against schema print("Validating values.yaml against schema.yaml...") -with open("values.yaml") as f: - values = yaml.safe_load(f) 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...") -with open("../tools/templates/lint-and-validate-values.yaml") as f: - lint_and_validate_values = yaml.safe_load(f) jsonschema.validate(lint_and_validate_values, schema) print("OK!") From 19c443348eee79353c4add2f8e64c2615134c22d Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 23:53:33 +0100 Subject: [PATCH 26/43] schema: add missing entry for hub.uid --- jupyterhub/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 5a9a928d65..ed9da8aa2a 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -39,6 +39,7 @@ hub: baseUrl: / cookieSecret: initContainers: [] + uid: fsGid: 1000 nodeSelector: {} tolerations: [] From 906ec1f801a0e15b00a2c1ee7eac0ef88aefc39b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Sun, 14 Feb 2021 23:54:35 +0100 Subject: [PATCH 27/43] tool: add tool to compare schema/values content --- tools/compare-values-schema-content.py | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 tools/compare-values-schema-content.py diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py new file mode 100755 index 0000000000..6d338aeb13 --- /dev/null +++ b/tools/compare-values-schema-content.py @@ -0,0 +1,93 @@ +#!/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. + +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. + + 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. +""" + +import jsonschema +import os +import sys +import yaml + +from collections.abc import MutableMapping + +# Change current directory to this directory +os.chdir(os.path.dirname(sys.argv[0])) + + +def reduce_schema(d): + """ + Takes a jsonschema loaded as a dictionary and return a reduced structure + ignoring everything apart from the structure it describes. + """ + 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 + return r + + +def flatten(d, parent_key="", sep="."): + """ + Takes a nested dictionary and return all keys flattened using a separator, + so one element returned would for example be "hub.image.tag". + """ + items = [] + for k, v in d.items(): + new_key = parent_key + sep + k if parent_key else k + if isinstance(v, MutableMapping): + if v: + 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 + + +def run(): + # Using these sets, we can validate further manually by printing the results + # of set operations. + with open("../jupyterhub/schema.yaml") as f: + schema = yaml.safe_load(f) + with open("../jupyterhub/values.yaml") as f: + values = yaml.safe_load(f) + # with open("templates/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) + + 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", + ) + + +run() From 8b58e8090e17d474c19c0da60691700b6e04f469 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 02:56:02 +0100 Subject: [PATCH 28/43] tool: add script to generate values.schema.json from schema.yaml --- .gitignore | 1 + tools/generate-json-schema.py | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100755 tools/generate-json-schema.py diff --git a/.gitignore b/.gitignore index cb9415537a..b546679582 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ ### Zero to JupyterHub Kubernetes ### +jupyterhub/values.schema.json tools/templates/rendered-templates/ bin/ .vagrant/ diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py new file mode 100755 index 0000000000..931b9b174e --- /dev/null +++ b/tools/generate-json-schema.py @@ -0,0 +1,61 @@ +#!/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. + +While we can directly generate a values.schema.json from 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. +""" + +import json +import os +import sys +import yaml + +from collections.abc import MutableMapping + +# Change current directory to this directory +os.chdir(os.path.dirname(sys.argv[0])) + + +def clean_jsonschema(d, parent_key=""): + """ + Modifies a dictionary representing a jsonschema in place to not contain + jsonschema keys not relevant for a values.schema.json file solely for use by + the helm CLI. + """ + JSONSCHEMA_KEYS_TO_REMOVE = {"description"} + + # start by cleaning up the current level + for k in set.intersection(JSONSCHEMA_KEYS_TO_REMOVE, set(d.keys())): + print(f"Removing key: {k}, from parent_key: {parent_key}") + del d[k] + + # Recursively cleanup nested levels, bypassing one level where there could + # be a valid Helm chart configuration named just like the jsonschema + # specific key to remove. + if "properties" in d: + for k, v in d["properties"].items(): + if isinstance(v, MutableMapping): + clean_jsonschema(v, k) + + +def run(): + # Using these sets, we can validate further manually by printing the results + # of set operations. + with open("../jupyterhub/schema.yaml") as f: + schema = yaml.safe_load(f) + + # Drop what isn't relevant for a values.schema.json file packaged with the + # Helm chart, such as the description keys only relevant for our + # configuration reference. + clean_jsonschema(schema) + + # dump schema to values.schema.json + with open("../jupyterhub/values.schema.json", "w") as f: + json.dump(schema, f) + + +run() From b6619a4e69c31373552e7dd6216d2f0c26539ca8 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 03:05:42 +0100 Subject: [PATCH 29/43] schema: give string / integer not in lists be allowed to be null --- jupyterhub/schema.yaml | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index fc6ab0488e..8f9d5f371d 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -792,7 +792,7 @@ properties: manifest as either the hub pod going into `Error` or `CrashLoopBackoff` states, or in some special cases, the hub running but... just doing very random things. Be careful! uid: - type: integer + type: [integer, "null"] minimum: 0 description: | DEPRECATED: Use hub.containerSecurityContext instead. @@ -805,7 +805,7 @@ properties: Defaults to 1000, which is the uid of the `jovyan` user that is present in the default hub image. fsGid: - type: integer + type: [integer, "null"] minimum: 0 description: The gid the hub process should be using when touching any volumes mounted. @@ -882,7 +882,7 @@ properties: Decides if a PodDisruptionBudget is created targeting the Deployment's pods. minAvailable: - type: integer + type: [integer, "null"] description: | The minimum number of pods required to be available during voluntary disruptions. @@ -944,10 +944,10 @@ properties: type: [boolean, "null"] description: *jupyterhub-native-config-description concurrentSpawnLimit: - type: integer + type: [integer, "null"] description: *jupyterhub-native-config-description consecutiveFailureLimit: - type: integer + type: [integer, "null"] description: *jupyterhub-native-config-description containerSecurityContext: &containerSecurityContext-spec type: object @@ -959,7 +959,7 @@ properties: type: object properties: type: - type: string + type: [string, "null"] description: | JupyterHub does not support running in parallel, due to this we default to using a deployment strategy of Recreate. @@ -1228,7 +1228,7 @@ properties: The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. acmeServer: - type: string + type: [string, "null"] description: | Let's Encrypt is one of various ACME servers that can provide a certificate, and by default their production server is used. @@ -1392,7 +1392,7 @@ properties: includeSubdomains: type: boolean maxAge: - type: integer + type: [integer, "null"] preload: type: boolean image: *image-spec @@ -1421,7 +1421,7 @@ properties: rollingUpdate: type: [string, "null"] type: - type: string + type: [string, "null"] description: | While the proxy pod running [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) @@ -1629,7 +1629,7 @@ properties: blockWithIptables: type: boolean ip: - type: string + type: [string, "null"] cmd: type: [array, string] description: &kubespawner-native-config-description | @@ -1690,7 +1690,7 @@ properties: type: [string, "null"] description: *kubespawner-native-config-description startTimeout: - type: integer + type: [integer, "null"] description: *kubespawner-native-config-description storage: type: object @@ -1700,7 +1700,7 @@ properties: [`singleuser.storage.type`](schema_singleuser.storage.type). properties: capacity: - type: string + type: [string, "null"] description: | Configures `KubeSpawner.storage_capacity`. @@ -1711,7 +1711,7 @@ properties: type: object properties: pvcNameTemplate: - type: string + type: [string, "null"] description: | Configures `KubeSpawner.pvc_name_template` which will be the resource name of the PVC created by KubeSpawner for each user @@ -1736,7 +1736,7 @@ properties: There is of a default StorageClass available in k8s clusters for use if this is unspecified. volumeNameTemplate: - type: string + type: [string, "null"] description: | Configures `KubeSpawner.volume_name_template`, which is the name to reference from the containers volumeMounts section. @@ -1749,7 +1749,7 @@ properties: extraVolumeMounts: *extraVolumeMounts-spec extraVolumes: *extraVolumes-spec homeMountPath: - type: string + type: [string, "null"] description: | The location within the container where the home folder storage should be mounted. @@ -1777,7 +1777,7 @@ properties: (dynamic), or if you want to attach existing storage (static), or don't want any storage to be attached (none). uid: - type: integer + type: [integer, "null"] description: | This dictates as what user the main container will start up as. @@ -1803,7 +1803,7 @@ properties: description: | Enables the user scheduler. replicas: - type: integer + type: [integer, "null"] description: | You can have multiple schedulers to share the workload or improve availability on node failure. @@ -1813,7 +1813,7 @@ properties: tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec logLevel: - type: integer + type: [integer, "null"] description: | Corresponds to the verbosity level of logging made by the kube-scheduler binary running within the user-scheduler pod. @@ -1900,11 +1900,11 @@ properties: default. This configuration option allows for the creation of such global default. defaultPriority: - type: integer + type: [integer, "null"] description: | The actual value for the default pod priority. userPlaceholderPriority: - type: integer + type: [integer, "null"] description: | The actual value for the user-placeholder pods' priority. userPlaceholder: @@ -1927,7 +1927,7 @@ properties: enabled: type: boolean replicas: - type: integer + type: [integer, "null"] description: | How many placeholder pods would you like to have? resources: @@ -2067,7 +2067,7 @@ properties: An infinite duration to wait for pods to schedule can be represented by `-1`. This was the default behavior of version 0.9.0 and earlier. - type: integer + type: [integer, "null"] nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec @@ -2158,16 +2158,16 @@ properties: type: boolean description: See the `--remove-named-servers` flag. timeout: - type: integer + type: [integer, "null"] description: See the `--timeout` flag. every: - type: integer + type: [integer, "null"] description: See the `--cull-every` flag. concurrency: - type: integer + type: [integer, "null"] description: See the `--concurrency` flag. maxAge: - type: integer + type: [integer, "null"] description: See the `--max-age` flag. debug: From 9c5ff086ca01f696315e143e1137093c981d5347 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 03:38:47 +0100 Subject: [PATCH 30/43] schema: allow null for some but not all values I decided that schema native values should always be set and not allowed to be null. But, for configuration values that are pass through configuration to JupyterHub and KubeSpawner for example, it made more sense to let them be null as the default of the Helm chart may be to not set it at all unless explicitly passed by a user. --- jupyterhub/schema.yaml | 106 +++++++++--------- .../proxy/autohttps/_configmap-dynamic.yaml | 2 +- jupyterhub/values.yaml | 20 ++-- 3 files changed, 66 insertions(+), 62 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 8f9d5f371d..0ffa237fef 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -108,7 +108,7 @@ properties: Toggle the automatic reference injection of the created Secret to all pods' `spec.imagePullSecrets` configuration. registry: - type: [string, "null"] + type: string description: | Name of the private registry you want to create a credential set for. It will default to Docker Hub's image registry. @@ -119,7 +119,7 @@ properties: - eu.gcr.io - alexmorreale.privatereg.net username: - type: [string, "null"] + type: string description: | Name of the user you want to use to connect to your private registry. @@ -130,7 +130,7 @@ properties: - alex@pfc.com - _json_key password: - type: [string, "null"] + type: string description: | Password for the private image registry's user. @@ -154,7 +154,7 @@ properties: Learn more in [this guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). email: - type: [string, "null"] + type: string description: | Specification of an email is most often not required, but it is supported. @@ -362,7 +362,7 @@ properties: as is required to avoid replacing the content of the entire directory we mount in. baseUrl: - type: [string, "null"] + type: string description: | This is the equivalent of c.JupyterHub.base_url, but it is also needed by the Helm chart in general. So, instead of setting @@ -425,7 +425,7 @@ properties: For more details, see the [Kubernetes documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes). cookieSecret: - type: [string, "null"] + type: string description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -456,7 +456,7 @@ properties: Set custom image name, tag, pullPolicy, or pullSecrets for the pod. properties: name: - type: [string, "null"] + type: string description: | The name of the image, without the tag. @@ -465,7 +465,7 @@ properties: gcr.io/my-project/my-image ``` tag: - type: [string, "null"] + type: string description: | The tag of the image to pull. This is the value following `:` in complete image specifications. @@ -476,7 +476,7 @@ properties: zhy270a ``` pullPolicy: - type: [string, "null"] + type: string enum: - "" - IfNotPresent @@ -537,7 +537,7 @@ properties: If you want to restrict egress, you can override this permissive default to be an empty list. interNamespaceAccessLabels: - type: [string, "null"] + type: string enum: - accept - ignore @@ -569,7 +569,7 @@ properties: type: object properties: type: - type: [string, "null"] + type: string enum: - sqlite-pvc - sqlite-memory @@ -661,7 +661,7 @@ properties: for more details about using a label selector for what PV to bind to. storage: - type: [string, "null"] + type: string description: | Size of disk to request for the database disk. accessModes: @@ -674,11 +674,11 @@ properties: documentation](https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1) for more information. storageClassName: - type: [string, "null"] + type: string description: | Name of the StorageClass required by the claim. subPath: - type: [string, "null"] + type: string description: | Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). @@ -689,13 +689,13 @@ properties: JupyterHub specific database schema if needed as part of a JupyterHub version upgrade. url: - type: [string, "null"] + type: string description: | Connection string when `hub.db.type` is mysql or postgres. See documentation for `hub.db.type` for more details on the format of this property. password: - type: [string, "null"] + type: string description: | Password for the database when `hub.db.type` is mysql or postgres. labels: @@ -847,7 +847,7 @@ properties: description: | Kubernetes annotations to apply to the hub service. loadBalancerIP: - type: [string, "null"] + type: string description: | A public IP address the hub Kubernetes service should be exposed on. To expose the hub directly is not recommended. Instead route @@ -882,12 +882,14 @@ properties: Decides if a PodDisruptionBudget is created targeting the Deployment's pods. minAvailable: + # FIXME: support maxUnavailable alongside minAvailable, making it + # make sense for both to be null also. type: [integer, "null"] description: | The minimum number of pods required to be available during voluntary disruptions. existingSecret: - type: [string, "null"] + type: string description: | Name of an existing k8s Secret to use instead of the chart managed k8s Secret. @@ -959,7 +961,7 @@ properties: type: object properties: type: - type: [string, "null"] + type: string description: | JupyterHub does not support running in parallel, due to this we default to using a deployment strategy of Recreate. @@ -1100,7 +1102,7 @@ properties: readinessProbe: *probe-spec resources: *resources-spec secretToken: - type: [string, "null"] + type: string description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -1131,7 +1133,7 @@ properties: the `proxy` pod and only accepting HTTP traffic on port 80. properties: type: - type: [string, "null"] + type: string enum: - ClusterIP - NodePort @@ -1182,7 +1184,7 @@ properties: documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core) for the structure of the items in this list. loadBalancerIP: - type: [string, "null"] + type: string description: | The public IP address the proxy-public Kubernetes service should be exposed on. This entry will end up at the configurable proxy @@ -1209,8 +1211,9 @@ properties: description: | Indicator to set whether HTTPS should be enabled or not on the proxy. Defaults to `true` if the https object is provided. type: - type: [string, "null"] + type: string enum: + - "" - letsencrypt - manual - offload @@ -1223,12 +1226,12 @@ properties: type: object properties: contactEmail: - type: [string, "null"] + type: string description: | The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. acmeServer: - type: [string, "null"] + type: string description: | Let's Encrypt is one of various ACME servers that can provide a certificate, and by default their production server is used. @@ -1242,7 +1245,7 @@ properties: See [Set up manual HTTPS](setup-manual-https) properties: key: - type: [string, "null"] + type: string description: | The RSA private key to be used for HTTPS. To be provided in the form of @@ -1254,7 +1257,7 @@ properties: -----END RSA PRIVATE KEY----- ``` cert: - type: [string, "null"] + type: string description: | The certificate to be used for HTTPS. To be provided in the form of @@ -1392,7 +1395,7 @@ properties: includeSubdomains: type: boolean maxAge: - type: [integer, "null"] + type: integer preload: type: boolean image: *image-spec @@ -1421,7 +1424,7 @@ properties: rollingUpdate: type: [string, "null"] type: - type: [string, "null"] + type: string description: | While the proxy pod running [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) @@ -1453,13 +1456,17 @@ properties: networkPolicy: *networkPolicy-spec podNameTemplate: type: [string, "null"] - description: | - Template for the pod name of each user, such as `jupyter-{username}{servername}`. + description: &kubespawner-native-config-description | + KubeSpawner native configuration, see the [KubeSpawner + documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) + for more information. cpu: type: object description: | Set CPU limits & guarantees that are enforced for each user. - See: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + + See the [Kubernetes docs](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) + for more info. properties: limit: type: [string, "null"] @@ -1629,19 +1636,16 @@ properties: blockWithIptables: type: boolean ip: - type: [string, "null"] + type: string cmd: - type: [array, string] - description: &kubespawner-native-config-description | - KubeSpawner native configuration, see the [KubeSpawner - documentation](https://jupyterhub-kubespawner.readthedocs.io/en/latest/spawner.html) - for more information. + type: [array, string, "null"] + description: *kubespawner-native-config-description defaultUrl: type: [string, "null"] description: *kubespawner-native-config-description # FIXME: name mismatch, named events_enabled in kubespawner events: - type: boolean + type: [boolean, "null"] description: *kubespawner-native-config-description extraAnnotations: type: object @@ -1749,7 +1753,7 @@ properties: extraVolumeMounts: *extraVolumeMounts-spec extraVolumes: *extraVolumes-spec homeMountPath: - type: [string, "null"] + type: string description: | The location within the container where the home folder storage should be mounted. @@ -1762,7 +1766,7 @@ properties: Configures `KubeSpawner.pvc_claim_name` to reference pre-existing storage. subPath: - type: [string, "null"] + type: string description: | Configures the `subPath` field of a `KubeSpawner.volume_mounts` entry added by the Helm chart. @@ -1770,7 +1774,7 @@ properties: Path within the volume from which the container's volume should be mounted. type: - type: [string, "null"] + type: string enum: [dynamic, static, none] description: | Decide if you want storage to be provisioned dynamically @@ -1803,7 +1807,7 @@ properties: description: | Enables the user scheduler. replicas: - type: [integer, "null"] + type: integer description: | You can have multiple schedulers to share the workload or improve availability on node failure. @@ -1813,7 +1817,7 @@ properties: tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec logLevel: - type: [integer, "null"] + type: integer description: | Corresponds to the verbosity level of logging made by the kube-scheduler binary running within the user-scheduler pod. @@ -1900,11 +1904,11 @@ properties: default. This configuration option allows for the creation of such global default. defaultPriority: - type: [integer, "null"] + type: integer description: | The actual value for the default pod priority. userPlaceholderPriority: - type: [integer, "null"] + type: integer description: | The actual value for the user-placeholder pods' priority. userPlaceholder: @@ -1927,7 +1931,7 @@ properties: enabled: type: boolean replicas: - type: [integer, "null"] + type: integer description: | How many placeholder pods would you like to have? resources: @@ -1949,7 +1953,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: [string, "null"] + type: string enum: - ignore - prefer @@ -1973,7 +1977,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: [string, "null"] + type: string enum: - ignore - prefer @@ -2009,7 +2013,7 @@ properties: description: | List of hosts to route requests to the proxy. pathSuffix: - type: [string, "null"] + type: string description: | Suffix added to Ingress's routing path pattern. @@ -2067,7 +2071,7 @@ properties: An infinite duration to wait for pods to schedule can be represented by `-1`. This was the default behavior of version 0.9.0 and earlier. - type: [integer, "null"] + type: integer nodeSelector: *nodeSelector-spec tolerations: *tolerations-spec containerSecurityContext: *containerSecurityContext-spec diff --git a/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml b/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml index 070a80e2dd..0e2a8f4123 100644 --- a/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml +++ b/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml @@ -19,7 +19,7 @@ http: headers: stsIncludeSubdomains: {{ .Values.proxy.traefik.hsts.includeSubdomains }} stsPreload: {{ .Values.proxy.traefik.hsts.preload }} - stsSeconds: {{ .Values.proxy.traefik.hsts.maxAge | int64 }} + stsSeconds: {{ .Values.proxy.traefik.hsts.maxAge }} # A middleware to redirect to https redirect: redirectScheme: diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index ed9da8aa2a..c74f09e511 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -35,9 +35,9 @@ hub: annotations: {} ports: nodePort: - loadBalancerIP: + loadBalancerIP: "" baseUrl: / - cookieSecret: + cookieSecret: "" initContainers: [] uid: fsGid: 1000 @@ -63,10 +63,10 @@ hub: accessModes: - ReadWriteOnce storage: 1Gi - subPath: - storageClassName: - url: - password: + subPath: "" + storageClassName: "" + url: "" + password: "" labels: {} annotations: {} command: [] @@ -135,7 +135,7 @@ hub: periodSeconds: 2 failureThreshold: 1000 timeoutSeconds: 1 - existingSecret: + existingSecret: "" rbac: enabled: true @@ -180,7 +180,7 @@ proxy: http: https: extraPorts: [] - loadBalancerIP: + loadBalancerIP: "" loadBalancerSourceRanges: [] # chp relates to the proxy pod, which is responsible for routing traffic based # on dynamic configuration sent from JupyterHub to CHP's REST API. @@ -282,8 +282,8 @@ proxy: # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE acmeServer: https://acme-v02.api.letsencrypt.org/directory manual: - key: - cert: + key: "" + cert: "" secret: name: "" key: tls.key From cf6c8c20b759c30891c06d2e7f4cf0a3c05fa16b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 03:54:05 +0100 Subject: [PATCH 31/43] ci: generate values.schema.json and run schema tests --- .github/workflows/test-chart.yaml | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 237d727a2e..04cfe3f259 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -24,7 +24,7 @@ on: workflow_dispatch: jobs: - lint_and_validate: + lint_and_validate_rendered_templates: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -50,6 +50,38 @@ jobs: run: tools/templates/lint-and-validate.py --strict continue-on-error: true + lint_and_validate_templates_with_schema: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + . ci/common + setup_helm + + - name: Generate values.schema.json + run: tools/generate-json-schema.py + + - name: Helm lint (values.yaml) + run: helm lint ./jupyterhub + + - name: Helm lint (lint-and-validate-values.yaml) + run: helm lint ./jupyterhub + + # FIXME: We can probably emit a GitHub workflow warning if these fail + # instead having them show as green without a warning or similar + - name: Helm lint --strict (values.yaml) + run: helm lint --strict ./jupyterhub + continue-on-error: true + + - name: Helm lint --strict (lint-and-validate-values.yaml) + run: helm lint --strict ./jupyterhub + continue-on-error: true + test: runs-on: ubuntu-20.04 timeout-minutes: 20 @@ -138,6 +170,11 @@ jobs: pip3 install --no-cache-dir -r dev-requirements.txt chartpress + # Generate values.schema.json from schema.yaml + - name: Generate values.schema.json from schema.yaml + run: | + tools/generate-json-schema.py + # Validate rendered helm templates against the k8s api-server with the # dedicated lint-and-validate-values.yaml config. - name: "Helm template --validate (with lint and validate config)" From 962dc5021a9a657d48091464e170b406b8a9e7de Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 04:00:52 +0100 Subject: [PATCH 32/43] tools/ci: don't forget to install pyyaml --- .github/workflows/publish.yml | 1 + .github/workflows/test-chart.yaml | 1 + tools/compare-values-schema-content.py | 3 ++- tools/generate-json-schema.py | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5c4bfd6e86..72b446ead9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -77,4 +77,5 @@ jobs: env: GITHUB_REPOSITORY: "${{ github.repository }}" run: | + ./tools/generate-json-schema.py ./ci/publish diff --git a/.github/workflows/test-chart.yaml b/.github/workflows/test-chart.yaml index 04cfe3f259..cc012635ec 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -62,6 +62,7 @@ jobs: run: | . ci/common setup_helm + pip install pyyaml - name: Generate values.schema.json run: tools/generate-json-schema.py diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py index 6d338aeb13..ef74e65cf8 100755 --- a/tools/compare-values-schema-content.py +++ b/tools/compare-values-schema-content.py @@ -18,10 +18,11 @@ import jsonschema import os import sys -import yaml from collections.abc import MutableMapping +import yaml + # Change current directory to this directory os.chdir(os.path.dirname(sys.argv[0])) diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index 931b9b174e..cbb955b8c3 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -12,10 +12,11 @@ import json import os import sys -import yaml from collections.abc import MutableMapping +import yaml + # Change current directory to this directory os.chdir(os.path.dirname(sys.argv[0])) From 1f20b3fdf13082979603b07977e08b80809fefed Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 04:03:29 +0100 Subject: [PATCH 33/43] tool: generate-json-schema, emit completion message --- tools/generate-json-schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index cbb955b8c3..bd310347bb 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -31,7 +31,6 @@ def clean_jsonschema(d, parent_key=""): # start by cleaning up the current level for k in set.intersection(JSONSCHEMA_KEYS_TO_REMOVE, set(d.keys())): - print(f"Removing key: {k}, from parent_key: {parent_key}") del d[k] # Recursively cleanup nested levels, bypassing one level where there could @@ -58,5 +57,7 @@ def run(): with open("../jupyterhub/values.schema.json", "w") as f: json.dump(schema, f) + print("jupyterhub/values.schema.json created") + run() From 4f30f417a0a5dfaba51e291cf3c48d6e1eea75b4 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 12:06:57 +0100 Subject: [PATCH 34/43] schema: fix storageClassName details --- jupyterhub/schema.yaml | 5 ++++- jupyterhub/values.yaml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 0ffa237fef..a50eb5b157 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -674,9 +674,12 @@ properties: documentation](https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1) for more information. storageClassName: - type: string + type: [string, "null"] description: | Name of the StorageClass required by the claim. + + If this is a blank string it will be set to a blank string, + while if it is null, it will not be set at all. subPath: type: string description: | diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index c74f09e511..42f8a41dc3 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -64,7 +64,7 @@ hub: - ReadWriteOnce storage: 1Gi subPath: "" - storageClassName: "" + storageClassName: url: "" password: "" labels: {} From 56688915c82f2ee87f2db13a15220c0f40f6366b Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 12:09:27 +0100 Subject: [PATCH 35/43] ci: correctly helm lint with lint-and-validate-values.yaml --- .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 cc012635ec..715a573bdb 100644 --- a/.github/workflows/test-chart.yaml +++ b/.github/workflows/test-chart.yaml @@ -71,7 +71,7 @@ jobs: run: helm lint ./jupyterhub - name: Helm lint (lint-and-validate-values.yaml) - run: helm lint ./jupyterhub + run: helm lint ./jupyterhub --values tools/templates/lint-and-validate-values.yaml # FIXME: We can probably emit a GitHub workflow warning if these fail # instead having them show as green without a warning or similar From c525ee8430f045f0bca04833a463a6414e28849f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 12:11:27 +0100 Subject: [PATCH 36/43] schema: accept null pullPolicy --- jupyterhub/schema.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index a50eb5b157..3e3efe8d03 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -476,7 +476,7 @@ properties: zhy270a ``` pullPolicy: - type: string + type: [string, "null"] enum: - "" - IfNotPresent From d5a5c1ef31cd62bf6664ce74358a2952ac74dad7 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Mon, 15 Feb 2021 12:44:46 +0100 Subject: [PATCH 37/43] schema tools: avoid use of os.chdir --- tools/compare-values-schema-content.py | 14 +++++++++----- tools/generate-json-schema.py | 11 +++++++---- tools/validate-against-schema.py | 14 +++++++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/tools/compare-values-schema-content.py b/tools/compare-values-schema-content.py index ef74e65cf8..d66ec505f7 100755 --- a/tools/compare-values-schema-content.py +++ b/tools/compare-values-schema-content.py @@ -23,8 +23,12 @@ import yaml -# Change current directory to this directory -os.chdir(os.path.dirname(sys.argv[0])) +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" +) def reduce_schema(d): @@ -67,11 +71,11 @@ def flatten(d, parent_key="", sep="."): def run(): # Using these sets, we can validate further manually by printing the results # of set operations. - with open("../jupyterhub/schema.yaml") as f: + with open(schema_yaml) as f: schema = yaml.safe_load(f) - with open("../jupyterhub/values.yaml") as f: + with open(values_yaml) as f: values = yaml.safe_load(f) - # with open("templates/lint-and-validate-values.yaml") as 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) diff --git a/tools/generate-json-schema.py b/tools/generate-json-schema.py index bd310347bb..0b8bf74c69 100755 --- a/tools/generate-json-schema.py +++ b/tools/generate-json-schema.py @@ -17,8 +17,11 @@ import yaml -# Change current directory to this directory -os.chdir(os.path.dirname(sys.argv[0])) +here_dir = os.path.abspath(os.path.dirname(__file__)) +schema_yaml = os.path.join(here_dir, os.pardir, "jupyterhub", "schema.yaml") +values_schema_json = os.path.join( + here_dir, os.pardir, "jupyterhub", "values.schema.json" +) def clean_jsonschema(d, parent_key=""): @@ -45,7 +48,7 @@ def clean_jsonschema(d, parent_key=""): def run(): # Using these sets, we can validate further manually by printing the results # of set operations. - with open("../jupyterhub/schema.yaml") as f: + with open(schema_yaml) as f: schema = yaml.safe_load(f) # Drop what isn't relevant for a values.schema.json file packaged with the @@ -54,7 +57,7 @@ def run(): clean_jsonschema(schema) # dump schema to values.schema.json - with open("../jupyterhub/values.schema.json", "w") as f: + with open(values_schema_json, "w") as f: json.dump(schema, f) print("jupyterhub/values.schema.json created") diff --git a/tools/validate-against-schema.py b/tools/validate-against-schema.py index c48d2782d4..d9e9cae10e 100755 --- a/tools/validate-against-schema.py +++ b/tools/validate-against-schema.py @@ -4,14 +4,18 @@ import sys import yaml -# Change current directory to this directory -os.chdir(os.path.dirname(sys.argv[0])) +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" +) -with open("../jupyterhub/schema.yaml") as f: +with open(schema_yaml) as f: schema = yaml.safe_load(f) -with open("../jupyterhub/values.yaml") as f: +with open(values_yaml) as f: values = yaml.safe_load(f) -with open("templates/lint-and-validate-values.yaml") as f: +with open(lint_and_validate_values_yaml) as f: lint_and_validate_values = yaml.safe_load(f) # Validate values.yaml against schema From d7188a20ee21c71ceb512bf688db41a4646880f2 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 01:58:17 +0100 Subject: [PATCH 38/43] docs: handle if/then in schema logic --- doc/source/conf.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/source/conf.py b/doc/source/conf.py index 8d9410674d..4e63ab5584 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -213,6 +213,9 @@ def parse_schema(d, md=[], depth=0, pre=""): Generate markdown headers from a passed python dictionary created by parsing a schema.yaml file. """ + if "then" in d: + d = d["then"] + if "properties" in d: depth += 1 # Create markdown headers for each schema level From dbdd49a9f2615e9a40d85ff615527f0f9e11cf11 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 02:01:33 +0100 Subject: [PATCH 39/43] microfix: ensure consistent behavior for baseUrl --- jupyterhub/templates/hub/service.yaml | 2 +- jupyterhub/templates/ingress.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/hub/service.yaml b/jupyterhub/templates/hub/service.yaml index 9370330477..d88f27f22d 100644 --- a/jupyterhub/templates/hub/service.yaml +++ b/jupyterhub/templates/hub/service.yaml @@ -9,7 +9,7 @@ metadata: prometheus.io/scrape: "true" {{- end }} {{- if not (index .Values.hub.service.annotations "prometheus.io/path") }} - prometheus.io/path: {{ .Values.hub.baseUrl }}hub/metrics + prometheus.io/path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/metrics {{- end }} {{- if not (index .Values.hub.service.annotations "prometheus.io/port") }} prometheus.io/port: "8081" diff --git a/jupyterhub/templates/ingress.yaml b/jupyterhub/templates/ingress.yaml index aaa8b84d2e..75c0afcb29 100644 --- a/jupyterhub/templates/ingress.yaml +++ b/jupyterhub/templates/ingress.yaml @@ -18,7 +18,7 @@ spec: {{- range $host := .Values.ingress.hosts | default (list "") }} - http: paths: - - path: {{ $.Values.hub.baseUrl }}{{ $.Values.ingress.pathSuffix }} + - path: {{ $.Values.hub.baseUrl | trimSuffix "/" }}/{{ $.Values.ingress.pathSuffix }} {{- if $.Capabilities.APIVersions.Has "networking.k8s.io/v1/Ingress" }} pathType: Prefix backend: From dfca9b57ea27af8d3e67f2a80f9743f57f375e7e Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 02:02:42 +0100 Subject: [PATCH 40/43] schema: require key/cert when proxy.https.type=manual --- jupyterhub/templates/proxy/secret.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jupyterhub/templates/proxy/secret.yaml b/jupyterhub/templates/proxy/secret.yaml index 9a3e4d6f8d..d9ff8ad185 100644 --- a/jupyterhub/templates/proxy/secret.yaml +++ b/jupyterhub/templates/proxy/secret.yaml @@ -8,6 +8,6 @@ metadata: {{- include "jupyterhub.labels" . | nindent 4 }} type: kubernetes.io/tls data: - tls.crt: {{ .Values.proxy.https.manual.cert | b64enc }} - tls.key: {{ .Values.proxy.https.manual.key | b64enc }} + tls.crt: {{ .Values.proxy.https.manual.cert | required "Required configuration missing: proxy.https.manual.cert" | b64enc }} + tls.key: {{ .Values.proxy.https.manual.key | required "Required configuration missing: proxy.https.manual.key" | b64enc }} {{- end }} From c1eae4e3a61967d895da74914cf91cf2f72b4f0f Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 02:05:16 +0100 Subject: [PATCH 41/43] values/schema: prefer null strings over blank strings --- jupyterhub/schema.yaml | 364 +++++++++--------- jupyterhub/templates/NOTES.txt | 2 +- jupyterhub/values.yaml | 52 +-- tools/templates/lint-and-validate-values.yaml | 11 + 4 files changed, 224 insertions(+), 205 deletions(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 3e3efe8d03..31d330901f 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1,4 +1,17 @@ -"$schema": http://json-schema.org/draft/2019-09/schema +# 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. +# +# 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! +# +# 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. +# +# ref: https://json-schema.org/learn/getting-started-step-by-step.html +# +$schema": http://json-schema.org/draft-07/schema# type: object required: - imagePullSecrets @@ -66,98 +79,105 @@ properties: imagePullSecret: type: object - description: | - This is configuration to create a k8s Secret resource of `type: - kubernetes.io/dockerconfigjson`, with credentials to pull images from a - private image registry. If you opt to do so, it will be available for use - by all pods in their respective `spec.imagePullSecrets` alongside other - k8s Secrets defined in `imagePullSecrets` or the pod respective - `...image.pullSecrets` configuration. - - In other words, using this configuration option can automate both the - otherwise manual creation of a k8s Secret and the otherwise manual - configuration to reference this k8s Secret in all the pods of the Helm - chart. - - ```sh - # you won't need to create a k8s Secret manually... - kubectl create secret docker-registry image-pull-secret \ - --docker-server= \ - --docker-username= \ - --docker-email= \ - --docker-password= - ``` - - 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. - properties: - create: - type: boolean - description: | - Toggle the creation of the k8s Secret with provided credentials to - access a private image registry. - automaticReferenceInjection: - type: boolean - description: | - Toggle the automatic reference injection of the created Secret to all - pods' `spec.imagePullSecrets` configuration. - registry: - type: string - description: | - Name of the private registry you want to create a credential set for. - It will default to Docker Hub's image registry. + required: [create] + if: + properties: + create: + const: true + then: + required: [registry, username, password] + description: | + This is configuration to create a k8s Secret resource of `type: + kubernetes.io/dockerconfigjson`, with credentials to pull images from a + private image registry. If you opt to do so, it will be available for use + by all pods in their respective `spec.imagePullSecrets` alongside other + k8s Secrets defined in `imagePullSecrets` or the pod respective + `...image.pullSecrets` configuration. + + In other words, using this configuration option can automate both the + otherwise manual creation of a k8s Secret and the otherwise manual + configuration to reference this k8s Secret in all the pods of the Helm + chart. + + ```sh + # you won't need to create a k8s Secret manually... + kubectl create secret docker-registry image-pull-secret \ + --docker-server= \ + --docker-username= \ + --docker-email= \ + --docker-password= + ``` + + 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. + properties: + create: + type: boolean + description: | + Toggle the creation of the k8s Secret with provided credentials to + access a private image registry. + automaticReferenceInjection: + type: boolean + description: | + Toggle the automatic reference injection of the created Secret to all + pods' `spec.imagePullSecrets` configuration. + registry: + type: string + description: | + Name of the private registry you want to create a credential set for. + It will default to Docker Hub's image registry. + + Examples: + - https://index.docker.io/v1/ + - quay.io + - eu.gcr.io + - alexmorreale.privatereg.net + username: + type: string + description: | + Name of the user you want to use to connect to your private registry. + + For external gcr.io, you will use the `_json_key`. + + Examples: + - alexmorreale + - alex@pfc.com + - _json_key + password: + type: string + description: | + Password for the private image registry's user. + + Examples: + - plaintextpassword + - abc123SECRETzyx098 + + For gcr.io registries the password will be a big JSON blob for a + Google cloud service account, it should look something like below. - Examples: - - https://index.docker.io/v1/ - - quay.io - - eu.gcr.io - - alexmorreale.privatereg.net - username: - type: string - description: | - Name of the user you want to use to connect to your private registry. - - For external gcr.io, you will use the `_json_key`. - - Examples: - - alexmorreale - - alex@pfc.com - - _json_key - password: - type: string - description: | - Password for the private image registry's user. - - Examples: - - plaintextpassword - - abc123SECRETzyx098 - - For gcr.io registries the password will be a big JSON blob for a - Google cloud service account, it should look something like below. - - ```yaml - password: |- - { - "type": "service_account", - "project_id": "jupyter-se", - "private_key_id": "f2ba09118a8d3123b3321bd9a7d6d0d9dc6fdb85", - ... - } - ``` + ```yaml + password: |- + { + "type": "service_account", + "project_id": "jupyter-se", + "private_key_id": "f2ba09118a8d3123b3321bd9a7d6d0d9dc6fdb85", + ... + } + ``` - Learn more in [this - guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). - email: - type: string - description: | - Specification of an email is most often not required, but it is - supported. + Learn more in [this + guide](http://docs.heptio.com/content/private-registries/pr-gcr.html). + email: + type: [string, "null"] + description: | + Specification of an email is most often not required, but it is + supported. imagePullSecrets: type: array @@ -174,6 +194,7 @@ properties: hub: type: object + required: [baseUrl] properties: config: type: object @@ -425,7 +446,7 @@ properties: For more details, see the [Kubernetes documentation](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes). cookieSecret: - type: string + type: [string, "null"] description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -452,6 +473,7 @@ properties: ``` image: &image-spec type: object + required: [name, tag] description: | Set custom image name, tag, pullPolicy, or pullSecrets for the pod. properties: @@ -476,12 +498,7 @@ properties: zhy270a ``` pullPolicy: - type: [string, "null"] - enum: - - "" - - IfNotPresent - - Always - - Never + enum: [null, "", IfNotPresent, Always, Never] description: | Configures the Pod's `spec.imagePullPolicy`. @@ -537,10 +554,7 @@ properties: If you want to restrict egress, you can override this permissive default to be an empty list. interNamespaceAccessLabels: - type: string - enum: - - accept - - ignore + enum: [accept, ignore] description: | This configuration option determines if both namespaces and pods in other namespaces, that have specific access labels, should be @@ -569,12 +583,7 @@ properties: type: object properties: type: - type: string - enum: - - sqlite-pvc - - sqlite-memory - - mysql - - postgres + enum: [sqlite-pvc, sqlite-memory, mysql, postgres] description: | Type of database backend to use for the hub database. @@ -637,6 +646,7 @@ properties: tables in the database specified. pvc: type: object + required: [storage] description: | Customize the Persistent Volume Claim used when `hub.db.type` is `sqlite-pvc`. properties: @@ -667,7 +677,7 @@ properties: accessModes: type: array items: - type: string + type: [string, "null"] description: | AccessModes contains the desired access modes the volume should have. See [the k8s @@ -681,7 +691,7 @@ properties: If this is a blank string it will be set to a blank string, while if it is null, it will not be set at all. subPath: - type: string + type: [string, "null"] description: | Path within the volume from which the container's volume should be mounted. Defaults to "" (volume's root). @@ -692,13 +702,13 @@ properties: JupyterHub specific database schema if needed as part of a JupyterHub version upgrade. url: - type: string + type: [string, "null"] description: | Connection string when `hub.db.type` is mysql or postgres. See documentation for `hub.db.type` for more details on the format of this property. password: - type: string + type: [string, "null"] description: | Password for the database when `hub.db.type` is mysql or postgres. labels: @@ -824,12 +834,7 @@ properties: Object to configure the service the JupyterHub will be exposed on by the Kubernetes server. properties: type: - type: [string, "null"] - enum: - - ClusterIP - - NodePort - - LoadBalancer - - ExternalName + enum: [ClusterIP, NodePort, LoadBalancer, ExternalName] description: | The Kubernetes ServiceType to be used. @@ -843,6 +848,7 @@ properties: properties: nodePort: type: [integer, "null"] + minimum: 0 description: | The nodePort to deploy the hub service on. annotations: @@ -850,7 +856,7 @@ properties: description: | Kubernetes annotations to apply to the hub service. loadBalancerIP: - type: string + type: [string, "null"] description: | A public IP address the hub Kubernetes service should be exposed on. To expose the hub directly is not recommended. Instead route @@ -892,7 +898,7 @@ properties: The minimum number of pods required to be available during voluntary disruptions. existingSecret: - type: string + type: [string, "null"] description: | Name of an existing k8s Secret to use instead of the chart managed k8s Secret. @@ -939,7 +945,7 @@ properties: documentation](https://jupyterhub.readthedocs.io/en/stable/api/app.html) for more information. allowNamedServers: - type: boolean + type: [boolean, "null"] description: *jupyterhub-native-config-description annotations: type: object @@ -964,7 +970,7 @@ properties: type: object properties: type: - type: string + type: [string, "null"] description: | JupyterHub does not support running in parallel, due to this we default to using a deployment strategy of Recreate. @@ -988,14 +994,20 @@ properties: Additional volumes for the Pod. Use a k8s native syntax. livenessProbe: &probe-spec type: object - description: | - This config option is exactly like the k8s native specification of a - container probe, except that it also supports an `enabled` boolean - flag. - - See [the k8s - documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#probe-v1-core) - for more details. + required: [enabled] + if: + properties: + enabled: + const: true + then: + description: | + This config option is exactly like the k8s native specification of a + container probe, except that it also supports an `enabled` boolean + flag. + + See [the k8s + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#probe-v1-core) + for more details. readinessProbe: *probe-spec namedServerLimitPerUser: type: [integer, "null"] @@ -1105,7 +1117,7 @@ properties: readinessProbe: *probe-spec resources: *resources-spec secretToken: - type: string + type: [string, "null"] description: | ```{note} As of version 1.0.0 this will automatically be generated and there is @@ -1136,12 +1148,7 @@ properties: the `proxy` pod and only accepting HTTP traffic on port 80. properties: type: - type: string - enum: - - ClusterIP - - NodePort - - LoadBalancer - - ExternalName + enum: [ClusterIP, NodePort, LoadBalancer, ExternalName] description: | Default `LoadBalancer`. See `hub.service.type` for supported values. labels: @@ -1187,7 +1194,7 @@ properties: documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#serviceport-v1-core) for the structure of the items in this list. loadBalancerIP: - type: string + type: [string, "null"] description: | The public IP address the proxy-public Kubernetes service should be exposed on. This entry will end up at the configurable proxy @@ -1210,17 +1217,11 @@ properties: For more information on configuring HTTPS for your JupyterHub, see the [HTTPS section in our security guide](https) properties: enabled: - type: boolean + type: [boolean, "null"] description: | Indicator to set whether HTTPS should be enabled or not on the proxy. Defaults to `true` if the https object is provided. type: - type: string - enum: - - "" - - letsencrypt - - manual - - offload - - secret + enum: [null, "", letsencrypt, manual, offload, secret] description: | The type of HTTPS encryption that is used. Decides on which ports and network policies are used for communication via HTTPS. Setting this to `secret` sets the type to manual HTTPS with a secret that has to be provided in the `https.secret` object. @@ -1229,12 +1230,12 @@ properties: type: object properties: contactEmail: - type: string + type: [string, "null"] description: | The contact email to be used for automatically provisioned HTTPS certificates by Let's Encrypt. For more information see [Set up automatic HTTPS](setup-automatic-https). Required for automatic HTTPS. acmeServer: - type: string + type: [string, "null"] description: | Let's Encrypt is one of various ACME servers that can provide a certificate, and by default their production server is used. @@ -1248,7 +1249,7 @@ properties: See [Set up manual HTTPS](setup-manual-https) properties: key: - type: string + type: [string, "null"] description: | The RSA private key to be used for HTTPS. To be provided in the form of @@ -1260,7 +1261,7 @@ properties: -----END RSA PRIVATE KEY----- ``` cert: - type: string + type: [string, "null"] description: | The certificate to be used for HTTPS. To be provided in the form of @@ -1382,6 +1383,7 @@ properties: extraVolumeMounts: *extraVolumeMounts-spec hsts: type: object + required: [includeSubdomains, maxAge, preload] description: | This section regards a HTTP Strict-Transport-Security (HSTS) response header. It can act as a request for a visiting web @@ -1427,7 +1429,7 @@ properties: rollingUpdate: type: [string, "null"] type: - type: string + type: [string, "null"] description: | While the proxy pod running [configurable-http-proxy](https://github.com/jupyterhub/configurable-http-proxy) @@ -1631,15 +1633,21 @@ properties: objects. cloudMetadata: type: object - description: | - Please refer to dedicated section in [the Helm chart - documentation](block-metadata-iptables) for more information about - this. - properties: - blockWithIptables: - type: boolean - ip: - type: string + required: [blockWithIptables] + if: + properties: + blockWithIptables: + const: true + then: + description: | + Please refer to dedicated section in [the Helm chart + documentation](block-metadata-iptables) for more information about + this. + properties: + blockWithIptables: + type: boolean + ip: + type: string cmd: type: [array, string, "null"] description: *kubespawner-native-config-description @@ -1701,6 +1709,7 @@ properties: description: *kubespawner-native-config-description storage: type: object + required: [type, homeMountPath] description: | This section configures KubeSpawner directly to some extent but also indirectly through Helm chart specific configuration options such as @@ -1726,7 +1735,7 @@ properties: storageAccessModes: type: array items: - type: string + type: [string, "null"] description: | Configures `KubeSpawner.storage_access_modes`. @@ -1769,7 +1778,7 @@ properties: Configures `KubeSpawner.pvc_claim_name` to reference pre-existing storage. subPath: - type: string + type: [string, "null"] description: | Configures the `subPath` field of a `KubeSpawner.volume_mounts` entry added by the Helm chart. @@ -1777,7 +1786,6 @@ properties: Path within the volume from which the container's volume should be mounted. type: - type: string enum: [dynamic, static, none] description: | Decide if you want storage to be provisioned dynamically @@ -1801,6 +1809,7 @@ properties: properties: userScheduler: type: object + required: [enabled, plugins, 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. @@ -1956,11 +1965,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: string - enum: - - ignore - - prefer - - require + enum: [ignore, prefer, require] description: | Decide if core pods *ignore*, *prefer* or *require* to schedule on nodes with this label: @@ -1980,11 +1985,7 @@ properties: label is preferred or even required? properties: matchNodePurpose: - type: string - enum: - - ignore - - prefer - - require + enum: [ignore, prefer, require] description: | Decide if user pods *ignore*, *prefer* or *require* to schedule on nodes with this label: @@ -1994,6 +1995,7 @@ properties: ingress: type: object + required: [enabled] properties: enabled: type: boolean @@ -2016,7 +2018,7 @@ properties: description: | List of hosts to route requests to the proxy. pathSuffix: - type: string + type: [string, "null"] description: | Suffix added to Ingress's routing path pattern. @@ -2032,6 +2034,7 @@ properties: prePuller: type: object + required: [hook, continuous] properties: annotations: type: object @@ -2050,11 +2053,12 @@ properties: containers in the namespace to have explicit resources set. extraTolerations: *tolerations-spec hook: + type: object + required: [enabled] description: | See the [*optimization section*](pulling-images-before-users-arrive) for more details. - type: object properties: enabled: type: boolean @@ -2081,6 +2085,8 @@ properties: image: *image-spec resources: *resources-spec continuous: + type: object + required: [enabled] description: | See the [*optimization section*](pulling-images-before-users-arrive) @@ -2090,7 +2096,6 @@ properties: If used with a Cluster Autoscaler (an autoscaling node pool), also add user-placeholders and enable pod priority. ``` - type: object properties: enabled: type: boolean @@ -2149,6 +2154,7 @@ properties: cull: type: object + required: [enabled] description: | The [jupyterhub-idle-culler](https://github.com/jupyterhub/jupyterhub-idle-culler) @@ -2159,10 +2165,10 @@ properties: description: | Enable/disable use of jupyter-idle-culler. users: - type: boolean + type: [boolean, "null"] description: See the `--cull-users` flag. removeNamedServers: - type: boolean + type: [boolean, "null"] description: See the `--remove-named-servers` flag. timeout: type: [integer, "null"] @@ -2179,6 +2185,7 @@ properties: debug: type: object + required: [enabled] properties: enabled: type: boolean @@ -2187,6 +2194,7 @@ properties: rbac: type: object + required: [enabled] properties: enabled: type: boolean diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt index 21bf8c2a4c..cb57ce2cd2 100644 --- a/jupyterhub/templates/NOTES.txt +++ b/jupyterhub/templates/NOTES.txt @@ -27,7 +27,7 @@ DEPRECATION: singleuser.cloudMetadata.enabled is deprecated, instead use singleu {{- /* Warn about an attempt to configure HTTPS but not having enabled it. */}} {{- if eq .Values.proxy.https.enabled false }} -{{- if or (not (eq .Values.proxy.https.type "letsencrypt")) (not (eq .Values.proxy.https.letsencrypt.contactEmail "")) }}{{ println }} +{{- if or (not (eq .Values.proxy.https.type "letsencrypt")) (not (eq (.Values.proxy.https.letsencrypt.contactEmail | default "") "")) }}{{ println }} WARNING: Configuring proxy.https without setting proxy.https.enabled to true is no longer allowed. {{- end }} {{- end }} diff --git a/jupyterhub/values.yaml b/jupyterhub/values.yaml index 42f8a41dc3..c00ff7e48b 100644 --- a/jupyterhub/values.yaml +++ b/jupyterhub/values.yaml @@ -12,10 +12,10 @@ custom: {} imagePullSecret: create: false automaticReferenceInjection: true - registry: "" - username: "" - email: "" - password: "" + registry: + username: + password: + email: # imagePullSecrets is configuration to reference the k8s Secret resources the # Helm chart's pods can get credentials from to pull their images. imagePullSecrets: [] @@ -35,9 +35,9 @@ hub: annotations: {} ports: nodePort: - loadBalancerIP: "" + loadBalancerIP: baseUrl: / - cookieSecret: "" + cookieSecret: initContainers: [] uid: fsGid: 1000 @@ -63,10 +63,10 @@ hub: accessModes: - ReadWriteOnce storage: 1Gi - subPath: "" + subPath: storageClassName: - url: "" - password: "" + url: + password: labels: {} annotations: {} command: [] @@ -81,7 +81,7 @@ hub: image: name: jupyterhub/k8s-hub tag: "set-by-chartpress" - pullPolicy: "" + pullPolicy: pullSecrets: [] resources: requests: @@ -135,7 +135,7 @@ hub: periodSeconds: 2 failureThreshold: 1000 timeoutSeconds: 1 - existingSecret: "" + existingSecret: rbac: enabled: true @@ -143,7 +143,7 @@ rbac: # proxy relates to the proxy pod, the proxy-public service, and the autohttps # pod and proxy-http service. proxy: - secretToken: "" + secretToken: annotations: {} deploymentStrategy: ## type: Recreate @@ -180,7 +180,7 @@ proxy: http: https: extraPorts: [] - loadBalancerIP: "" + loadBalancerIP: loadBalancerSourceRanges: [] # chp relates to the proxy pod, which is responsible for routing traffic based # on dynamic configuration sent from JupyterHub to CHP's REST API. @@ -192,7 +192,7 @@ proxy: image: name: jupyterhub/configurable-http-proxy tag: 4.2.2 - pullPolicy: "" + pullPolicy: pullSecrets: [] extraCommandLineFlags: [] livenessProbe: @@ -233,7 +233,7 @@ proxy: image: name: traefik tag: v2.4.2 # ref: https://hub.docker.com/_/traefik?tab=tags - pullPolicy: "" + pullPolicy: pullSecrets: [] hsts: includeSubdomains: false @@ -269,7 +269,7 @@ proxy: image: name: jupyterhub/k8s-secret-sync tag: "set-by-chartpress" - pullPolicy: "" + pullPolicy: pullSecrets: [] resources: {} labels: {} @@ -278,14 +278,14 @@ proxy: type: letsencrypt #type: letsencrypt, manual, offload, secret letsencrypt: - contactEmail: "" + contactEmail: # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE acmeServer: https://acme-v02.api.letsencrypt.org/directory manual: - key: "" - cert: "" + key: + cert: secret: - name: "" + name: key: tls.key crt: tls.crt hosts: [] @@ -309,7 +309,7 @@ singleuser: image: name: jupyterhub/k8s-network-tools tag: "set-by-chartpress" - pullPolicy: "" + pullPolicy: pullSecrets: [] cloudMetadata: # block set to true will append a privileged initContainer using the @@ -363,7 +363,7 @@ singleuser: image: name: jupyterhub/k8s-singleuser-sample tag: "set-by-chartpress" - pullPolicy: "" + pullPolicy: pullSecrets: [] startTimeout: 300 cpu: @@ -422,7 +422,7 @@ scheduling: # that we have forked. name: k8s.gcr.io/kube-scheduler tag: v1.19.7 - pullPolicy: "" + pullPolicy: pullSecrets: [] nodeSelector: {} tolerations: [] @@ -472,7 +472,7 @@ prePuller: image: name: jupyterhub/k8s-image-awaiter tag: "set-by-chartpress" - pullPolicy: "" + pullPolicy: pullSecrets: [] containerSecurityContext: runAsUser: 65534 # nobody user @@ -497,14 +497,14 @@ prePuller: image: name: k8s.gcr.io/pause tag: "3.2" # https://console.cloud.google.com/gcr/images/google-containers/GLOBAL/pause?gcrImageListsize=30 - pullPolicy: "" + pullPolicy: pullSecrets: [] ingress: enabled: false annotations: {} hosts: [] - pathSuffix: "" + pathSuffix: tls: [] cull: diff --git a/tools/templates/lint-and-validate-values.yaml b/tools/templates/lint-and-validate-values.yaml index f31442ca66..237e05e035 100644 --- a/tools/templates/lint-and-validate-values.yaml +++ b/tools/templates/lint-and-validate-values.yaml @@ -79,6 +79,8 @@ hub: annotations: mock: mock image: + name: dummy-name + tag: dummy-tag pullSecrets: [c] initContainers: - name: mock-init-container-name @@ -371,6 +373,7 @@ scheduling: userScheduler: enabled: true replicas: 1 + logLevel: 10 image: name: gcr.io/google_containers/kube-scheduler-amd64 tag: v1.11.2 @@ -385,6 +388,14 @@ scheduling: operator: "Equal" value: "mock-taint-to-tolerates-value" effect: "NoExecute" + plugins: + score: + disabled: + - name: SelectorSpread + enabled: + - name: NodePreferAvoidPods + weight: 161051 + - name: NodeAffinity podPriority: enabled: true userPlaceholder: From b76335af12edea940ae2f58eda29fb42f95c2dff Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 02:13:36 +0100 Subject: [PATCH 42/43] schema: remove too ambitious FIXME --- jupyterhub/schema.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/jupyterhub/schema.yaml b/jupyterhub/schema.yaml index 31d330901f..8fb1a3fac3 100644 --- a/jupyterhub/schema.yaml +++ b/jupyterhub/schema.yaml @@ -1699,7 +1699,6 @@ properties: is set to true. properties: image: *image-spec - # FIXME: no resources here # FIXME: name mismatch, named service_account in kubespawner serviceAccountName: type: [string, "null"] From 6c2a38a2b08ab77dc3253bd97362455e84850871 Mon Sep 17 00:00:00 2001 From: Erik Sundell Date: Wed, 17 Feb 2021 02:14:30 +0100 Subject: [PATCH 43/43] fix: accept user-placeholder resources --- .../templates/scheduling/user-placeholder/statefulset.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml index d33e274529..1a6aefb909 100644 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -46,8 +46,11 @@ spec: - name: pause image: {{ .Values.prePuller.pause.image.name }}:{{ .Values.prePuller.pause.image.tag }} resources: - # FIXME: should render scheduling.userPlaceholder.resources if specified + {{- if .Values.scheduling.userPlaceholder.resources }} + {{- .Values.scheduling.userPlaceholder.resources | toYaml | trimSuffix "\n" | nindent 12 }} + {{- else }} {{- include "jupyterhub.resources" . | nindent 12 }} + {{- end }} {{- with .Values.scheduling.userPlaceholder.containerSecurityContext }} securityContext: {{- . | toYaml | trimSuffix "\n" | nindent 12 }}