diff --git a/README.md b/README.md index ab6ed1a..2257123 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,17 @@ With a JupyterHub [SSH](https://www.ssh.com/ssh) server deployed, you can start [SFTP](https://www.ssh.com/ssh/sftp) server deployed alongside the JupyterHub's user storage, you can use SFTP to work against your JupyterHub user's home directory. These services are authenticated using an access token acquired from your JupyterHub's user interface under `/hub/token`. +## What does this fork have? +This project a forked project from yuvipanda/jupyterhub_ssh and I was able to directly contribute to create a fork to support one small feature. + +Current jupyterhub-ssh by yuvipanda only supports ssh connection to unnamed server. I added few lines to support named server as well. + +The way it'd work is if you have a named server, you'd simply do `ssh user-namedserver@hub_url` + +It does create the named server automactically if it doesn't exist, which you'll be able to view in the JupyterHub UI as well. + +All the other features remain the same and you can still connect to unnamed server by just `user@hub_url` + ## Development Status This project is under active develpoment :tada:, so expect a few changes along the way. @@ -204,3 +215,8 @@ proxy: 4. Enter the token received from JupyterHub as a password. 5. TADA :tada: Now you can transfer files to and from your home directory on the hubs. + + +# Disclaimer +I do not own this project. Please look at official project at https://github.com/yuvipanda/jupyterhub-ssh +I just added a few lines of code to support named servers. \ No newline at end of file diff --git a/jupyterhub_ssh/__init__.py b/jupyterhub_ssh/__init__.py index 6415113..fe09835 100644 --- a/jupyterhub_ssh/__init__.py +++ b/jupyterhub_ssh/__init__.py @@ -40,15 +40,22 @@ async def get_user_server_url(self, session, username): Else return None """ + # Checking if username has any dash char "-" which would be then have the name of the named server the user wants to connect to + # For example username would look like admin-mytestserver. Any characters can be used here but chose - just for the sake of it + # If the server name itself has "-" then we would need to split only once. Splitting with split(char, #Occurrence) + dash_char = "-" + server_name = "" + if dash_char in username: + username_with_server = username.split(dash_char, 1) + username = username_with_server[0] + server_name = username_with_server[1] async with session.get(self.app.hub_url / "hub/api/users" / username) as resp: if resp.status != 200: return None user = await resp.json() - print(user) - # URLs will have preceding slash, but yarl forbids those server = user.get("servers", {}).get("", {}) if server.get("ready", False): - return self.app.hub_url / user["servers"][""]["url"][1:] + return self.app.hub_url / user["servers"][server_name if server_name!="" else ""]["url"][1:] else: return None @@ -56,7 +63,20 @@ async def start_user_server(self, session, username): """ """ # REST API reference: https://jupyterhub.readthedocs.io/en/stable/_static/rest-api/index.html#operation--users--name--server-post # REST API implementation: https://github.com/jupyterhub/jupyterhub/blob/187fe911edce06eb067f736eaf4cc9ea52e69e08/jupyterhub/apihandlers/users.py#L451-L497 - create_url = self.app.hub_url / "hub/api/users" / username / "server" + + # Checking if username has any dash char "-" which would be then have the name of the named server the user wants to connect to + # For example username would look like admin-mytestserver. Any characters can be used here but chose - just for the sake of it + # If the server name itself has "-" then we would need to split only once. Splitting with split(char, #Occurrence)dash_char = "-" + server_name = "" + if dash_char in username: + username_with_server = username.split(dash_char, 1) + username = username_with_server[0] + server_name = username_with_server[1] + + if server_name: + create_url = self.app.hub_url / "hub/api/users" / username / "servers" / server_name + else: + create_url = self.app.hub_url / "hub/api/users" / username / "server" async with session.post(create_url) as resp: if resp.status == 201 or resp.status == 400: @@ -68,6 +88,8 @@ async def start_user_server(self, session, username): # We manually generate this, even though it's *bad* # Mostly because when the server is already running, JupyterHub # doesn't respond with the whole model! + if server_name: + return self.app.hub_url / "user" / username / server_name return self.app.hub_url / "user" / username elif resp.status == 202: # Server start has been requested, now and potentially earlier, @@ -81,6 +103,10 @@ async def start_user_server(self, session, username): while notebook_url is None: # FIXME: Exponential backoff + make this configurable await asyncio.sleep(0.5) + if server_name: + notebook_url = await self.get_user_server_url( + session, username + "/" + server_name + ) notebook_url = await self.get_user_server_url( session, username ) @@ -152,6 +178,7 @@ async def _handle_client(self, stdin, stdout, stderr): """ Handle data transfer once session has been fully established. """ + print ("Notebook URL looks like: ", self.notebook_url) async with ClientSession() as client, Terminado( self.notebook_url, self.token, client ) as terminado: