Repository: jupyterhub/kubespawner

Issue:
Long user names used in pod and pvc labels not allowed by k8s
The `hub.jupyter.org/username` label in pod or pvc manifests (e.g. https://github.com/jupyterhub/kubespawner/blob/master/kubespawner/spawner.py#L663) when user name has more than 63 characters will make the creation of the pod fail with a error like this:

error: invalid label value: "hub.jupyter.org/username=notebook-a8e986f5974c1ee896fa7717cad98511be0388b3d667535d9af7fe18feab7e3d": must be no more than 63 characters

The username I'm using comes from an external OAuth2 IdP and has a fixed length of 64 characters so any pod creation will  always fail. As a workaround I have changed the label to `hub.jupyter.org/userid` and use the id which should be small enough. However I'm not sure if that's the right approach. 



Discussion:

Patch:
diff --git a/kubespawner/objects.py b/kubespawner/objects.py
index ae0d42e..2c8201c 100644
--- a/kubespawner/objects.py
+++ b/kubespawner/objects.py
@@ -250,7 +250,8 @@ def make_pvc(
     storage_class,
     access_modes,
     storage,
-    labels
+    labels,
+    annotations={}
     ):
     """
     Make a k8s pvc specification for running a user notebook.
@@ -272,9 +273,7 @@ def make_pvc(
     pvc.api_version = "v1"
     pvc.metadata = V1ObjectMeta()
     pvc.metadata.name = name
-    pvc.metadata.annotations = {}
-    if storage_class:
-        pvc.metadata.annotations.update({"volume.beta.kubernetes.io/storage-class": storage_class})
+    pvc.metadata.annotations = annotations
     pvc.metadata.labels = {}
     pvc.metadata.labels.update(labels)
     pvc.spec = V1PersistentVolumeClaimSpec()
@@ -282,6 +281,10 @@ def make_pvc(
     pvc.spec.resources = V1ResourceRequirements()
     pvc.spec.resources.requests = {"storage": storage}
 
+    if storage_class:
+        pvc.metadata.annotations.update({"volume.beta.kubernetes.io/storage-class": storage_class})
+        pvc.spec.storage_class_name = storage_class
+
     return pvc
 
 def make_ingress(
diff --git a/kubespawner/spawner.py b/kubespawner/spawner.py
index e3da4d9..163303a 100644
--- a/kubespawner/spawner.py
+++ b/kubespawner/spawner.py
@@ -783,12 +783,8 @@ class KubeSpawner(Spawner):
         labels = {
             'heritage': 'jupyterhub',
             'app': 'jupyterhub',
-            'hub.jupyter.org/username': escapism.escape(self.user.name)
         }
 
-        if self.name:
-            # FIXME: Make sure this is dns safe?
-            labels['hub.jupyter.org/servername'] = self.name
         labels.update(extra_labels)
         return labels
 
@@ -801,6 +797,17 @@ class KubeSpawner(Spawner):
         labels.update(self.pod_reflector.labels)
         return self._build_common_labels(labels)
 
+    def _build_common_annotations(self, extra_annotations):
+        # Annotations don't need to be escaped
+        annotations = {
+            'hub.jupyter.org/username': self.user.name
+        }
+        if self.name:
+            annotations['hub.jupyter.org/servername'] = self.name
+
+        annotations.update(extra_annotations)
+        return annotations
+
     @gen.coroutine
     def get_pod_manifest(self):
         """
@@ -822,7 +829,7 @@ class KubeSpawner(Spawner):
             real_cmd = None
 
         labels = self._build_pod_labels(self._expand_all(self.singleuser_extra_labels))
-        annotations = self._expand_all(self.singleuser_extra_annotations)
+        annotations = self._build_common_annotations(self._expand_all(self.singleuser_extra_annotations))
 
         return make_pod(
             name=self.pod_name,
@@ -861,12 +868,15 @@ class KubeSpawner(Spawner):
         """
         labels = self._build_common_labels(self._expand_all(self.user_storage_extra_labels))
 
+        annotations = self._build_common_annotations({})
+
         return make_pvc(
             name=self.pvc_name,
             storage_class=self.user_storage_class,
             access_modes=self.user_storage_access_modes,
             storage=self.user_storage_capacity,
-            labels=labels
+            labels=labels,
+            annotations=annotations
         )
 
     def is_pod_running(self, pod):


Test patch:
diff --git a/tests/test_objects.py b/tests/test_objects.py
index a9f0311..d69b8d3 100644
--- a/tests/test_objects.py
+++ b/tests/test_objects.py
@@ -787,6 +787,7 @@ def test_make_resources_all():
             }
         },
         'spec': {
+            'storageClassName': 'gce-standard-storage',
             'accessModes': ['ReadWriteOnce'],
             'resources': {
                 'requests': {