Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

propagation support for bind mounts (once docker-py supports it officially) #494

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
9 changes: 6 additions & 3 deletions dockerspawner/dockerspawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1429,12 +1429,15 @@ def _fmt(v):
return self.format_volume_name(v, self)

for k, v in volumes.items():
m = mode
rw = mode
p = 'rprivate'
if isinstance(v, dict):
if "mode" in v:
m = v["mode"]
rw = v["mode"]
if "propagation" in v:
p = v["propagation"]
v = v["bind"]
binds[_fmt(k)] = {"bind": _fmt(v), "mode": m}
binds[_fmt(k)] = {"bind": _fmt(v), "mode": rw, 'propagation': p}
return binds

def _render_templates(self, obj, ns=None):
Expand Down
38 changes: 37 additions & 1 deletion dockerspawner/systemuserspawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ class SystemUserSpawner(DockerSpawner):
),
)

image_homedir_propagation = Unicode(
"rprivate",
config=True,
help=dedent(
"""
Mode for bind mount propagation. Possible values are
`rshared`,`shared`,`rprivate`, `private`, `rslave`, `slave`.
default is `rprivate`.
This is only interpreted by docker-py if patched (see:
https://github.com/jannefleischer/docker-py/commit/786d55de465e72d0bb4b318272a2d020e43ff54a
Copy link
Member

Choose a reason for hiding this comment

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

If it required a manual patch, I would not consider this patch appropriate for this project, but since it's merged, can you reference the required version of docker-py here? It's not clear to me what that is.

) and run on linux - no support for Docker Desktop.
"""
),
)

run_as_root = Bool(
False,
config=True,
Expand Down Expand Up @@ -117,6 +132,23 @@ def volume_mount_points(self):
mount_points.append(self.homedir)
return mount_points

@property
def homedirpropagation(self):
"""
Setting for the propagation mode for home dir. Wrong values are defaulted to `rprivate`.
"""
if self.image_homedir_propagation in (
'rshared',
'shared',
'rprivate',
'private',
'rslave',
'slave',
):
return self.image_homedir_propagation
else:
return 'rprivate'

@property
def volume_binds(self):
"""
Expand All @@ -129,7 +161,11 @@ def volume_binds(self):
}
"""
volumes = super().volume_binds
volumes[self.host_homedir] = {'bind': self.homedir, 'ro': False}
volumes[self.host_homedir] = {
'bind': self.homedir,
'ro': False,
'propagation': self.homedirpropagation,
}
return volumes

def get_env(self):
Expand Down
28 changes: 22 additions & 6 deletions tests/volumes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ def test_binds(monkeypatch):
d.user = types.SimpleNamespace(name="xyz")
d.volumes = {"a": "b", "c": {"bind": "d", "mode": "Z"}}
assert d.volume_binds == {
"a": {"bind": "b", "mode": "rw"},
"c": {"bind": "d", "mode": "Z"},
"a": {"bind": "b", "mode": "rw", "propagation": 'rprivate'},
"c": {"bind": "d", "mode": "Z", "propagation": 'rprivate'},
}
d.volumes = {"a": "b", "c": "d", "e": "f"}
assert d.volume_mount_points == ["b", "d", "f"]
d.volumes = {"/nfs/{username}": {"bind": "/home/{username}", "mode": "z"}}
assert d.volume_binds == {"/nfs/xyz": {"bind": "/home/xyz", "mode": "z"}}
assert d.volume_binds == {
"/nfs/xyz": {"bind": "/home/xyz", "mode": "z", "propagation": 'rprivate'}
}
assert d.volume_mount_points == ["/home/xyz"]


Expand All @@ -35,7 +37,13 @@ def test_format(label_template, spawner):
return "THIS IS A TEST"

d.format_volume_name = test_format
assert d.volume_binds == {"THIS IS A TEST": {"bind": "THIS IS A TEST", "mode": "z"}}
assert d.volume_binds == {
"THIS IS A TEST": {
"bind": "THIS IS A TEST",
"mode": "z",
"propagation": 'rprivate',
}
}
assert d.volume_mount_points == ["THIS IS A TEST"]


Expand All @@ -46,7 +54,11 @@ def test_default_format_volume_name(monkeypatch):
d.user = types.SimpleNamespace(name="user@email.com")
d.volumes = {"data/{username}": {"bind": "/home/{raw_username}", "mode": "z"}}
assert d.volume_binds == {
"data/user-40email-2ecom": {"bind": "/home/user@email.com", "mode": "z"}
"data/user-40email-2ecom": {
"bind": "/home/user@email.com",
"mode": "z",
"propagation": 'rprivate',
}
}
assert d.volume_mount_points == ["/home/user@email.com"]

Expand All @@ -60,7 +72,11 @@ def test_escaped_format_volume_name(monkeypatch):
d.volumes = {"data/{username}": {"bind": "/home/{username}", "mode": "z"}}
d.format_volume_name = dockerspawner.volumenamingstrategy.escaped_format_volume_name
assert d.volume_binds == {
"data/user-40email-2ecom": {"bind": "/home/user-40email-2ecom", "mode": "z"}
"data/user-40email-2ecom": {
"bind": "/home/user-40email-2ecom",
"mode": "z",
"propagation": 'rprivate',
}
}
assert d.volume_mount_points == ["/home/user-40email-2ecom"]

Expand Down
Loading