Date: Mon, 11 Nov 2019 18:33:52 +0100
Subject: [PATCH 15/21] Show container expiration date and gpu indices in the
"Info" dialog instead of directly next to the name
---
resources/jupyterhub-mod/template-admin.html | 1 -
.../mlhubspawner/mlhubspawner/mlhubspawner.py | 6 ----
resources/mlhubspawner/mlhubspawner/utils.py | 31 +++++++------------
3 files changed, 11 insertions(+), 27 deletions(-)
diff --git a/resources/jupyterhub-mod/template-admin.html b/resources/jupyterhub-mod/template-admin.html
index 258ebc3..3a07bbc 100644
--- a/resources/jupyterhub-mod/template-admin.html
+++ b/resources/jupyterhub-mod/template-admin.html
@@ -49,7 +49,6 @@
{%- if spawner.name -%}
/{{ spawner.name }}
{%- endif -%}
- {{ spawner.get_container_metadata() if spawner.get_container_metadata is defined }} {# comment {{ current_user.spawner_class().get_days_to_live() }} #}
diff --git a/resources/mlhubspawner/mlhubspawner/mlhubspawner.py b/resources/mlhubspawner/mlhubspawner/mlhubspawner.py
index 384d46d..7a2f0c7 100644
--- a/resources/mlhubspawner/mlhubspawner/mlhubspawner.py
+++ b/resources/mlhubspawner/mlhubspawner/mlhubspawner.py
@@ -293,12 +293,6 @@ def connect_hub_to_network(self, network):
"Could not connect mlhub to the network and, thus, cannot create the container.")
return
- def get_container_metadata(self) -> str:
- if self.container_id is None or self.container_id == '':
- return ""
-
- return utils.get_container_metadata(self)
-
def get_workspace_config(self) -> str:
return utils.get_workspace_config(self)
diff --git a/resources/mlhubspawner/mlhubspawner/utils.py b/resources/mlhubspawner/mlhubspawner/utils.py
index d362daf..e9506b2 100644
--- a/resources/mlhubspawner/mlhubspawner/utils.py
+++ b/resources/mlhubspawner/mlhubspawner/utils.py
@@ -29,23 +29,6 @@
def get_lifetime_timestamp(labels: dict) -> float:
return float(labels.get(LABEL_EXPIRATION_TIMESTAMP, '0'))
-def get_container_metadata(spawner):
- meta_information = []
- container_labels = spawner.get_labels()
- lifetime_timestamp = get_lifetime_timestamp(container_labels)
- if lifetime_timestamp != 0:
- difference_in_days = math.ceil((lifetime_timestamp - time.time())/60/60/24)
- meta_information.append("Expires: {}d".format(difference_in_days))
-
- nvidia_visible_devices = container_labels.get(LABEL_NVIDIA_VISIBLE_DEVICES, "")
- if nvidia_visible_devices != "":
- meta_information.append("GPUs: {}".format(nvidia_visible_devices))
-
- if len(meta_information) == 0:
- return ""
-
- return "({})".format(", ".join(meta_information))
-
def init_docker_client(client_kwargs: dict, tls_config: dict) -> docker.DockerClient:
"""Create a docker client.
The configuration is done the same way DockerSpawner initializes the low-level API client.
@@ -74,7 +57,15 @@ def load_state(spawner, state):
spawner.saved_user_options = state.get("saved_user_options")
def get_workspace_config(spawner) -> str:
- if not hasattr(spawner, "saved_user_options"):
- return "{}"
+ workspace_config = {}
+ if hasattr(spawner, "saved_user_options"):
+ workspace_config = {**spawner.saved_user_options}
+
+ # Add remaining lifetime information
+ lifetime_timestamp = get_lifetime_timestamp(spawner.get_labels())
+ if lifetime_timestamp != 0:
+ difference_in_seconds = math.ceil(lifetime_timestamp - time.time())
+ difference_in_days = math.ceil(difference_in_seconds/60/60/24)
+ workspace_config.update({"remaining_lifetime_seconds": difference_in_seconds, "remaining_lifetime_days": difference_in_days})
- return json.dumps(spawner.saved_user_options)
+ return json.dumps(workspace_config)
From f607d9500d231fec6aaebf17dd432e49ffc922a1 Mon Sep 17 00:00:00 2001
From: Benjamin Raethlein
Date: Mon, 11 Nov 2019 19:06:51 +0100
Subject: [PATCH 16/21] Make the username normalization more error resistant
---
resources/jupyterhub_config.py | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/resources/jupyterhub_config.py b/resources/jupyterhub_config.py
index 4dc388b..12d23d5 100644
--- a/resources/jupyterhub_config.py
+++ b/resources/jupyterhub_config.py
@@ -24,8 +24,19 @@
original_normalize_username = Authenticator.normalize_username
def custom_normalize_username(self, username):
username = original_normalize_username(self, username)
- for forbidden_username_char in [" ", ",", ";", "."]:
- username = username.replace(forbidden_username_char, "")
+ more_than_one_forbidden_char = False
+ for forbidden_username_char in [" ", ",", ";", ".", "-"]:
+ # Replace special characters with a non-special character. Cannot just be empty, like "", because then it could happen that two distinct user names are transformed into the same username.
+ # Example: "foo, bar" and "fo, obar" would both become "foobar".
+ replace_char = "0"
+ # If there is more than one special character, just replace one of them. Otherwise, "foo, bar" would become "foo00bar" instead of "foo0bar"
+ if more_than_one_forbidden_char == True:
+ replace_char = ""
+ temp_username = username
+ username = username.replace(forbidden_username_char, replace_char)
+ if username != temp_username:
+ more_than_one_forbidden_char = True
+
return username
Authenticator.normalize_username = custom_normalize_username
From 7cbbd994d4d08fcdfa272e3b388c2bf6daf1f853 Mon Sep 17 00:00:00 2001
From: Benjamin Raethlein
Date: Mon, 11 Nov 2019 19:40:46 +0100
Subject: [PATCH 17/21] Show "SSH Access" button also in home.html
---
Dockerfile | 1 +
.../jupyterhub-mod/ssh-dialog-snippet.html | 38 +++++++++++++++++++
resources/jupyterhub-mod/template-admin.html | 38 ++-----------------
resources/jupyterhub-mod/template-home.html | 7 +++-
4 files changed, 49 insertions(+), 35 deletions(-)
create mode 100644 resources/jupyterhub-mod/ssh-dialog-snippet.html
diff --git a/Dockerfile b/Dockerfile
index c127b31..155d389 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -133,6 +133,7 @@ COPY resources/logo.png /usr/local/share/jupyterhub/static/images/jupyter.png
COPY resources/jupyterhub_config.py $_RESOURCES_PATH/jupyterhub_config.py
COPY resources/jupyterhub-mod/template-home.html /usr/local/share/jupyterhub/templates/home.html
COPY resources/jupyterhub-mod/template-admin.html /usr/local/share/jupyterhub/templates/admin.html
+COPY resources/jupyterhub-mod/ssh-dialog-snippet.html /usr/local/share/jupyterhub/templates/ssh-dialog-snippet.html
COPY resources/jupyterhub-mod/info-dialog-snippet.html /usr/local/share/jupyterhub/templates/info-dialog-snippet.html
COPY resources/jupyterhub-mod/jsonpresenter /usr/local/share/jupyterhub/static/components/jsonpresenter/
COPY resources/jupyterhub-mod/cleanup-service.py /resources/cleanup-service.py
diff --git a/resources/jupyterhub-mod/ssh-dialog-snippet.html b/resources/jupyterhub-mod/ssh-dialog-snippet.html
new file mode 100644
index 0000000..6b65b24
--- /dev/null
+++ b/resources/jupyterhub-mod/ssh-dialog-snippet.html
@@ -0,0 +1,38 @@
+
+
+
+
+{% call modal('SSH Setup', btn_label="Copy to Clipboard", btn_class='btn-primary copy-clipboard-button') %}
+ Execute this command to setup the SSH connection to server :
+
+{% endcall %}
diff --git a/resources/jupyterhub-mod/template-admin.html b/resources/jupyterhub-mod/template-admin.html
index 3a07bbc..61fc45d 100644
--- a/resources/jupyterhub-mod/template-admin.html
+++ b/resources/jupyterhub-mod/template-admin.html
@@ -147,10 +147,7 @@
{% endcall %}
{# CUSTOM STUFF #}
-{% call modal('SSH Setup', btn_label="Copy to Clipboard", btn_class='btn-primary copy-clipboard-button') %}
- Execute this command to setup the SSH connection to server :
-
-{% endcall %}
+
{# END CUSTOM STUFF #}
@@ -183,38 +180,11 @@
{% block script %}
{{ super() }}
+{% include 'ssh-dialog-snippet.html' %}
+
{% include 'info-dialog-snippet.html' %}
{% endblock %}
diff --git a/resources/jupyterhub-mod/template-home.html b/resources/jupyterhub-mod/template-home.html
index 64ad196..a4627aa 100644
--- a/resources/jupyterhub-mod/template-home.html
+++ b/resources/jupyterhub-mod/template-home.html
@@ -83,7 +83,7 @@
|