Skip to content

Commit

Permalink
Manage multiple PVCs via the default JH Spawner UI
Browse files Browse the repository at this point in the history
* Extend default Spawner UI with two new elements: Workspace and Data Volumes
* Extend `config.yaml` of the default UI with default values and
  options for the new Spawner form fields
* Add asynchronous logic for handling K8s-native API requests in
  `spawner.py` (e.g. provision new PVCs, retrieve existing PVCs)
* Add 'storageClass` ksonnet parameter to easily configure which PVC
  provisioner will be used - defaults to 'none'
* Remove `disks` and `notebookPVCMount` ksonnet parameters, along with
  related assignments in `jupyterhub_config.py`
* Remove buggy code for attaching multiple PVCs in `jupyterhub_config.py`
* Fix flake8 linting errors in `spawner.py`

Closes kubeflow#34
Closes kubeflow#541

Signed-off-by: Ioannis Androulidakis <[email protected]>
Signed-off-by: Ilias Tsitsimpis <[email protected]>
  • Loading branch information
ioandr committed Nov 7, 2018
1 parent c6a42bb commit 9b06b0e
Show file tree
Hide file tree
Showing 9 changed files with 717 additions and 85 deletions.
8 changes: 2 additions & 6 deletions kubeflow/core/jupyterhub.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,6 @@
},
],
env: std.prune([
{
name: "NOTEBOOK_PVC_MOUNT",
value: params.notebookPVCMount,
},
{
name: "KF_AUTHENTICATOR",
value: params.jupyterHubAuthenticator,
Expand All @@ -163,8 +159,8 @@
value: params.useJupyterLabAsDefault,
},
{
name: "KF_PVC_LIST",
value: params.disks,
name: "STORAGE_CLASS",
value: params.storageClass,
},
if params.platform == "gke" then
{
Expand Down
59 changes: 16 additions & 43 deletions kubeflow/core/jupyterhub_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
c.KubeSpawner.singleuser_uid = 1000
c.KubeSpawner.singleuser_fs_gid = 100
c.KubeSpawner.singleuser_working_dir = '/home/jovyan'
volumes = []
volume_mounts = []

# Allow environment vars to override uid and gid.
# This allows local host path mounts to be read/writable
Expand Down Expand Up @@ -65,34 +63,23 @@ def modify_pod_hook(spawner, pod):
###################################################
# Persistent volume options
###################################################
# Using persistent storage requires a default storage class.
# TODO(jlewi): Verify this works on minikube.
# see https://github.com/kubeflow/kubeflow/pull/22#issuecomment-350500944
pvc_mount = os.environ.get('NOTEBOOK_PVC_MOUNT')
if pvc_mount and pvc_mount != 'null':
c.KubeSpawner.user_storage_pvc_ensure = True
c.KubeSpawner.storage_pvc_ensure = True
# How much disk space do we want?
c.KubeSpawner.user_storage_capacity = '10Gi'
c.KubeSpawner.storage_capacity = '10Gi'
c.KubeSpawner.pvc_name_template = 'claim-{username}{servername}'
volumes.append(
{
'name': 'volume-{username}{servername}',
'persistentVolumeClaim': {
'claimName': 'claim-{username}{servername}'
}
}
)
volume_mounts.append(
{
'mountPath': pvc_mount,
'name': 'volume-{username}{servername}'
}
)

# Set user_storage_pvc_ensure to False to prevent KubeSpawner from handling PVCs
# We natively handle PVCs via KubeFormSpawner and its dedicated methods

# NOTE: user_storage_pvc_ensure has been deprecated in a future release
c.KubeSpawner.storage_pvc_ensure = False
c.KubeSpawner.user_storage_pvc_ensure = False

volumes = []
volume_mounts = []
c.KubeSpawner.volumes = volumes
c.KubeSpawner.volume_mounts = volume_mounts

storage_class = None
if os.environ.get('STORAGE_CLASS') != 'null':
storage_class = os.environ.get('STORAGE_CLASS')

# Set both service_account and singleuser_service_account because
# singleuser_service_account has been deprecated in a future release
c.KubeSpawner.service_account = 'jupyter-notebook'
Expand All @@ -107,21 +94,6 @@ def modify_pod_hook(spawner, pod):
if os.environ.get('DEFAULT_JUPYTERLAB').lower() == 'true':
c.KubeSpawner.default_url = '/lab'

# PVCs
pvcs = os.environ.get('KF_PVC_LIST')
if pvcs and pvcs != 'null':
for pvc in pvcs.split(','):
volumes.append({
'name': pvc,
'persistentVolumeClaim': {
'claimName': pvc
}
})
volume_mounts.append({
'name': pvc,
'mountPath': '/mnt/' + pvc
})

gcp_secret_name = os.environ.get('GCP_SECRET_NAME')
if gcp_secret_name:
volumes.append({
Expand All @@ -137,5 +109,6 @@ def modify_pod_hook(spawner, pod):

# Set extra spawner configuration variables
c.KubeSpawner.extra_spawner_config = {
'gcp_secret_name': gcp_secret_name
'gcp_secret_name': gcp_secret_name,
'storage_class': storage_class
}
3 changes: 1 addition & 2 deletions kubeflow/core/prototypes/jupyterhub.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
// @optionalParam image string gcr.io/kubeflow/jupyterhub-k8s:v20180531-3bb991b1 The image to use for JupyterHub.
// @optionalParam jupyterHubAuthenticator string null The authenticator to use
// @optionalParam useJupyterLabAsDefault string false Set JupterLab interface as the default
// @optionalParam notebookPVCMount string /home/jovyan Mount path for PVC. Set empty to disable PVC
// @optionalParam disks string null Comma separated list of Google persistent disks to attach to jupyter environments.
// @optionalParam gcpSecretName string user-gcp-sa The name of the secret containing service account credentials for GCP
// @optionalParam notebookUid string -1 UserId of the host user for minikube local fs mount
// @optionalParam notebookGid string -1 GroupID of the host user for minikube local fs mount
// @optionalParam accessLocalFs string false Set true if mounting a local fs directory that needs to be accessed by Jupyter Notebook in Minikube.
// @optionalParam ui string default The JupyterHub Spawner User Interface
// @optionalParam storageClass string null The storageClass to use for PVC management

local jupyterhub = import "kubeflow/core/jupyterhub.libsonnet";
local instance = jupyterhub.new(env, params);
Expand Down
11 changes: 4 additions & 7 deletions kubeflow/core/tests/jupyterhub_test.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ local params = {
name: "jupyterhub",
platform: "gke",
serviceType: "ClusterIP",
disks: "null",
gcpSecretName: "user-gcp-sa",
image: "gcr.io/kubeflow/jupyterhub-k8s:v20180531-3bb991b1",
jupyterHubAuthenticator: "iap",
useJupyterLabAsDefault: true,
notebookPVCMount: "/home/jovyan",
notebookUid: "-1",
notebookGid: "-1",
accessLocalFs: "false",
ui: "default",
storageClass: "null",
};
local env = {
namespace: "foo",
Expand Down Expand Up @@ -98,10 +97,6 @@ std.assertEqual(
"/etc/config/jupyterhub_config.py",
],
env: [
{
name: "NOTEBOOK_PVC_MOUNT",
value: "/home/jovyan",
},
{
name: "KF_AUTHENTICATOR",
value: "iap",
Expand All @@ -111,7 +106,7 @@ std.assertEqual(
value: true,
},
{
name: "KF_PVC_LIST",
name: "STORAGE_CLASS",
value: "null",
},
{
Expand Down Expand Up @@ -172,13 +167,15 @@ std.assertEqual(
resources: [
"pods",
"persistentvolumeclaims",
"secrets",
],
verbs: [
"get",
"watch",
"list",
"create",
"delete",
"patch",
],
},
{
Expand Down
15 changes: 15 additions & 0 deletions kubeflow/core/ui/default/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,20 @@ spawnerFormDefaults:
value: '1.0'
memory:
value: 1.0Gi
workspaceVolume:
value:
type:
value: New
name:
value: workspace
size:
value: '10'
mountPath:
readOnly: true
value: /home/jovyan
accessModes:
value: ReadWriteOnce
dataVolumes:
value: []
extraResources:
value: ''
Loading

0 comments on commit 9b06b0e

Please sign in to comment.