Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@
install_requires=[
"jupyterhub>=0.9",
"tornado>=5.0",
"escapism"
],
)
31 changes: 23 additions & 8 deletions systemdspawner/systemdspawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import subprocess
from traitlets import Bool, Unicode, List, Dict
import asyncio
import string

import escapism
from systemdspawner import systemd

from jupyterhub.spawner import Spawner
Expand Down Expand Up @@ -53,11 +55,11 @@ class SystemdSpawner(Spawner):
).tag(config=True)

unit_name_template = Unicode(
'jupyter-{USERNAME}-singleuser',
'jupyter-{USERNAME}-singleuser-{SERVERNAME}',
help="""
Template to use to make the systemd service names.

{USERNAME} and {USERID} are expanded}
{USERNAME}, {SERVERNAME}, and {USERID} are expanded}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{USERID} has been replaced by {USERNAME} for the unit name, so it can be removed as it's not longer used.

"""
).tag(config=True)

Expand Down Expand Up @@ -147,25 +149,37 @@ class SystemdSpawner(Spawner):
"""
).tag(config=True)

# Characters that are safe for systemd units.
safe_chars = set(string.ascii_lowercase + string.digits + string.ascii_uppercase + '_-')

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# All traitlets configurables are configured by now
self.unit_name = self._expand_user_vars(self.unit_name_template)

self.log.debug('user:%s Initialized spawner with unit %s', self.user.name, self.unit_name)

def _expand_user_vars(self, string):
def _escape_variable(self, in_string):
"""
Escape variables for systemd unit naming
"""
return escapism.escape(in_string, safe=self.safe_chars, escape_char='-')

def _expand_user_vars(self, in_string):
"""
Expand user related variables in a given string

Currently expands:
{USERNAME} -> Name of the user
{USERID} -> UserID
{SERVERNAME} -> Name of the server (self.name)
"""
return string.format(
USERNAME=self.user.name,
USERID=self.user.id
)
# Strip the trailing - if we don't have a name.
return in_string.format(
USERNAME=self._escape_variable(self.user.name),
USERID=self.user.id,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

USERID can be removed from here also, as it's not used anymore for the unit name.

SERVERNAME=self._escape_variable(self.name)
).rstrip('-')

def get_state(self):
"""
Expand Down Expand Up @@ -205,7 +219,8 @@ async def start(self):
# from earlier. Regardless, we kill it and start ours in its place.
# FIXME: Carefully look at this when doing a security sweep.
if await systemd.service_running(self.unit_name):
self.log.info('user:%s Unit %s already exists but not known to JupyterHub. Killing', self.user.name, self.unit_name)
self.log.info('user:%s Unit %s already exists but not known to JupyterHub. Killing', self.user.name,
self.unit_name)
await systemd.stop_service(self.unit_name)
if await systemd.service_running(self.unit_name):
self.log.error('user:%s Could not stop already existing unit %s', self.user.name, self.unit_name)
Expand Down