mirror of https://github.com/docker/docker-py.git
volume: added support for bind propagation
https://docs.docker.com/storage/bind-mounts/#configure-bind-propagation Signed-off-by: Janne Jakob Fleischer <janne.fleischer@ils-forschung.de> Signed-off-by: Milas Bowman <milas.bowman@docker.com>
This commit is contained in:
parent
8b9ad7807f
commit
bea63224e0
|
@ -319,6 +319,11 @@ class ContainerApiMixin:
|
||||||
'/var/www': {
|
'/var/www': {
|
||||||
'bind': '/mnt/vol1',
|
'bind': '/mnt/vol1',
|
||||||
'mode': 'ro',
|
'mode': 'ro',
|
||||||
|
},
|
||||||
|
'/autofs/user1': {
|
||||||
|
'bind': '/mnt/vol3',
|
||||||
|
'mode': 'rw',
|
||||||
|
'propagation': 'shared'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -329,10 +334,11 @@ class ContainerApiMixin:
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
container_id = client.api.create_container(
|
container_id = client.api.create_container(
|
||||||
'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'],
|
'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2', '/mnt/vol3'],
|
||||||
host_config=client.api.create_host_config(binds=[
|
host_config=client.api.create_host_config(binds=[
|
||||||
'/home/user1/:/mnt/vol2',
|
'/home/user1/:/mnt/vol2',
|
||||||
'/var/www:/mnt/vol1:ro',
|
'/var/www:/mnt/vol1:ro',
|
||||||
|
'/autofs/user1:/mnt/vol3:rw,shared',
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ from ..tls import TLSConfig
|
||||||
|
|
||||||
from urllib.parse import urlparse, urlunparse
|
from urllib.parse import urlparse, urlunparse
|
||||||
|
|
||||||
|
|
||||||
URLComponents = collections.namedtuple(
|
URLComponents = collections.namedtuple(
|
||||||
'URLComponents',
|
'URLComponents',
|
||||||
'scheme netloc url params query fragment',
|
'scheme netloc url params query fragment',
|
||||||
|
@ -141,6 +140,22 @@ def convert_volume_binds(binds):
|
||||||
else:
|
else:
|
||||||
mode = 'rw'
|
mode = 'rw'
|
||||||
|
|
||||||
|
# NOTE: this is only relevant for Linux hosts
|
||||||
|
# (doesn't apply in Docker Desktop)
|
||||||
|
propagation_modes = [
|
||||||
|
'rshared',
|
||||||
|
'shared',
|
||||||
|
'rslave',
|
||||||
|
'slave',
|
||||||
|
'rprivate',
|
||||||
|
'private',
|
||||||
|
]
|
||||||
|
if 'propagation' in v and v['propagation'] in propagation_modes:
|
||||||
|
if mode:
|
||||||
|
mode = ','.join([mode, v['propagation']])
|
||||||
|
else:
|
||||||
|
mode = v['propagation']
|
||||||
|
|
||||||
result.append(
|
result.append(
|
||||||
f'{k}:{bind}:{mode}'
|
f'{k}:{bind}:{mode}'
|
||||||
)
|
)
|
||||||
|
|
|
@ -542,6 +542,24 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
||||||
inspect_data = self.client.inspect_container(container)
|
inspect_data = self.client.inspect_container(container)
|
||||||
self.check_container_data(inspect_data, False)
|
self.check_container_data(inspect_data, False)
|
||||||
|
|
||||||
|
def test_create_with_binds_rw_rshared(self):
|
||||||
|
self.run_with_volume_propagation(
|
||||||
|
False,
|
||||||
|
'rshared',
|
||||||
|
TEST_IMG,
|
||||||
|
['touch', os.path.join(self.mount_dest, self.filename)],
|
||||||
|
)
|
||||||
|
container = self.run_with_volume_propagation(
|
||||||
|
True,
|
||||||
|
'rshared',
|
||||||
|
TEST_IMG,
|
||||||
|
['ls', self.mount_dest],
|
||||||
|
)
|
||||||
|
logs = self.client.logs(container).decode('utf-8')
|
||||||
|
assert self.filename in logs
|
||||||
|
inspect_data = self.client.inspect_container(container)
|
||||||
|
self.check_container_data(inspect_data, True, 'rshared')
|
||||||
|
|
||||||
@requires_api_version('1.30')
|
@requires_api_version('1.30')
|
||||||
def test_create_with_mounts(self):
|
def test_create_with_mounts(self):
|
||||||
mount = docker.types.Mount(
|
mount = docker.types.Mount(
|
||||||
|
@ -597,7 +615,7 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
||||||
assert mount['Source'] == mount_data['Name']
|
assert mount['Source'] == mount_data['Name']
|
||||||
assert mount_data['RW'] is True
|
assert mount_data['RW'] is True
|
||||||
|
|
||||||
def check_container_data(self, inspect_data, rw):
|
def check_container_data(self, inspect_data, rw, propagation='rprivate'):
|
||||||
assert 'Mounts' in inspect_data
|
assert 'Mounts' in inspect_data
|
||||||
filtered = list(filter(
|
filtered = list(filter(
|
||||||
lambda x: x['Destination'] == self.mount_dest,
|
lambda x: x['Destination'] == self.mount_dest,
|
||||||
|
@ -607,6 +625,7 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
||||||
mount_data = filtered[0]
|
mount_data = filtered[0]
|
||||||
assert mount_data['Source'] == self.mount_origin
|
assert mount_data['Source'] == self.mount_origin
|
||||||
assert mount_data['RW'] == rw
|
assert mount_data['RW'] == rw
|
||||||
|
assert mount_data['Propagation'] == propagation
|
||||||
|
|
||||||
def run_with_volume(self, ro, *args, **kwargs):
|
def run_with_volume(self, ro, *args, **kwargs):
|
||||||
return self.run_container(
|
return self.run_container(
|
||||||
|
@ -624,6 +643,23 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def run_with_volume_propagation(self, ro, propagation, *args, **kwargs):
|
||||||
|
return self.run_container(
|
||||||
|
*args,
|
||||||
|
volumes={self.mount_dest: {}},
|
||||||
|
host_config=self.client.create_host_config(
|
||||||
|
binds={
|
||||||
|
self.mount_origin: {
|
||||||
|
'bind': self.mount_dest,
|
||||||
|
'ro': ro,
|
||||||
|
'propagation': propagation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network_mode='none'
|
||||||
|
),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ArchiveTest(BaseAPIIntegrationTest):
|
class ArchiveTest(BaseAPIIntegrationTest):
|
||||||
def test_get_file_archive_from_container(self):
|
def test_get_file_archive_from_container(self):
|
||||||
|
|
Loading…
Reference in New Issue