mirror of https://github.com/docker/docker-py.git
Merge 07e35d3f5f
into db7f8b8bb6
This commit is contained in:
commit
534ad571d2
|
@ -235,6 +235,9 @@ class Mount(dict):
|
|||
``default```, ``consistent``, ``cached``, ``delegated``.
|
||||
propagation (string): A propagation mode with the value ``[r]private``,
|
||||
``[r]shared``, or ``[r]slave``. Only valid for the ``bind`` type.
|
||||
recursive (string): Bind mount recursive mode, one of ``enabled``,
|
||||
``disabled``, ``writable``, or ``readonly``. Only valid for the
|
||||
``bind`` type.
|
||||
no_copy (bool): False if the volume should be populated with the data
|
||||
from the target. Default: ``False``. Only valid for the ``volume``
|
||||
type.
|
||||
|
@ -247,9 +250,9 @@ class Mount(dict):
|
|||
"""
|
||||
|
||||
def __init__(self, target, source, type='volume', read_only=False,
|
||||
consistency=None, propagation=None, no_copy=False,
|
||||
labels=None, driver_config=None, tmpfs_size=None,
|
||||
tmpfs_mode=None):
|
||||
consistency=None, propagation=None, recursive=None,
|
||||
no_copy=False, labels=None, driver_config=None,
|
||||
tmpfs_size=None, tmpfs_mode=None):
|
||||
self['Target'] = target
|
||||
self['Source'] = source
|
||||
if type not in ('bind', 'volume', 'tmpfs', 'npipe'):
|
||||
|
@ -267,6 +270,21 @@ class Mount(dict):
|
|||
self['BindOptions'] = {
|
||||
'Propagation': propagation
|
||||
}
|
||||
if recursive is not None:
|
||||
bind_options = self.setdefault('BindOptions', {})
|
||||
if recursive == "enabled":
|
||||
pass # noop - default
|
||||
elif recursive == "disabled":
|
||||
bind_options['NonRecursive'] = True
|
||||
elif recursive == "writable":
|
||||
bind_options['ReadOnlyNonRecursive'] = True
|
||||
elif recursive == "readonly":
|
||||
bind_options['ReadOnlyForceRecursive'] = True
|
||||
else:
|
||||
raise errors.InvalidArgument(
|
||||
'Invalid recursive bind option, must be one of '
|
||||
'"enabled", "disabled", "writable", or "readonly".'
|
||||
)
|
||||
if any([labels, driver_config, no_copy, tmpfs_size, tmpfs_mode]):
|
||||
raise errors.InvalidArgument(
|
||||
'Incompatible options have been provided for the bind '
|
||||
|
@ -282,7 +300,7 @@ class Mount(dict):
|
|||
volume_opts['DriverConfig'] = driver_config
|
||||
if volume_opts:
|
||||
self['VolumeOptions'] = volume_opts
|
||||
if any([propagation, tmpfs_size, tmpfs_mode]):
|
||||
if any([propagation, recursive, tmpfs_size, tmpfs_mode]):
|
||||
raise errors.InvalidArgument(
|
||||
'Incompatible options have been provided for the volume '
|
||||
'type mount.'
|
||||
|
@ -299,7 +317,7 @@ class Mount(dict):
|
|||
tmpfs_opts['SizeBytes'] = parse_bytes(tmpfs_size)
|
||||
if tmpfs_opts:
|
||||
self['TmpfsOptions'] = tmpfs_opts
|
||||
if any([propagation, labels, driver_config, no_copy]):
|
||||
if any([propagation, recursive, labels, driver_config, no_copy]):
|
||||
raise errors.InvalidArgument(
|
||||
'Incompatible options have been provided for the tmpfs '
|
||||
'type mount.'
|
||||
|
|
|
@ -598,6 +598,60 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
|||
inspect_data = self.client.inspect_container(container)
|
||||
self.check_container_data(inspect_data, False)
|
||||
|
||||
@requires_api_version('1.41')
|
||||
def test_create_with_mounts_recursive_disabled(self):
|
||||
mount = docker.types.Mount(
|
||||
type="bind", source=self.mount_origin, target=self.mount_dest,
|
||||
read_only=True, recursive="disabled"
|
||||
)
|
||||
host_config = self.client.create_host_config(mounts=[mount])
|
||||
container = self.run_container(
|
||||
TEST_IMG, ['ls', self.mount_dest],
|
||||
host_config=host_config
|
||||
)
|
||||
assert container
|
||||
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, False,
|
||||
bind_options_field="NonRecursive")
|
||||
|
||||
@requires_api_version('1.44')
|
||||
def test_create_with_mounts_recursive_writable(self):
|
||||
mount = docker.types.Mount(
|
||||
type="bind", source=self.mount_origin, target=self.mount_dest,
|
||||
read_only=True, recursive="writable"
|
||||
)
|
||||
host_config = self.client.create_host_config(mounts=[mount])
|
||||
container = self.run_container(
|
||||
TEST_IMG, ['ls', self.mount_dest],
|
||||
host_config=host_config
|
||||
)
|
||||
assert container
|
||||
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, False,
|
||||
bind_options_field="ReadOnlyNonRecursive")
|
||||
|
||||
@requires_api_version('1.44')
|
||||
def test_create_with_mounts_recursive_ro(self):
|
||||
mount = docker.types.Mount(
|
||||
type="bind", source=self.mount_origin, target=self.mount_dest,
|
||||
read_only=True, recursive="readonly"
|
||||
)
|
||||
host_config = self.client.create_host_config(mounts=[mount])
|
||||
container = self.run_container(
|
||||
TEST_IMG, ['ls', self.mount_dest],
|
||||
host_config=host_config
|
||||
)
|
||||
assert container
|
||||
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, False,
|
||||
bind_options_field="ReadOnlyForceRecursive")
|
||||
|
||||
@requires_api_version('1.30')
|
||||
def test_create_with_volume_mount(self):
|
||||
mount = docker.types.Mount(
|
||||
|
@ -620,7 +674,8 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
|||
assert mount['Source'] == mount_data['Name']
|
||||
assert mount_data['RW'] is True
|
||||
|
||||
def check_container_data(self, inspect_data, rw, propagation='rprivate'):
|
||||
def check_container_data(self, inspect_data, rw, propagation='rprivate',
|
||||
bind_options_field=None):
|
||||
assert 'Mounts' in inspect_data
|
||||
filtered = list(filter(
|
||||
lambda x: x['Destination'] == self.mount_dest,
|
||||
|
@ -631,6 +686,18 @@ class VolumeBindTest(BaseAPIIntegrationTest):
|
|||
assert mount_data['Source'] == self.mount_origin
|
||||
assert mount_data['RW'] == rw
|
||||
assert mount_data['Propagation'] == propagation
|
||||
if bind_options_field:
|
||||
assert 'Mounts' in inspect_data['HostConfig']
|
||||
mounts = [
|
||||
x for x in inspect_data['HostConfig']['Mounts']
|
||||
if x['Target'] == self.mount_dest
|
||||
]
|
||||
assert len(mounts) == 1
|
||||
mount = mounts[0]
|
||||
assert 'BindOptions' in mount
|
||||
bind_options = mount['BindOptions']
|
||||
assert bind_options_field in bind_options
|
||||
assert bind_options[bind_options_field] is True
|
||||
|
||||
def run_with_volume(self, ro, *args, **kwargs):
|
||||
return self.run_container(
|
||||
|
|
Loading…
Reference in New Issue