mirror of
https://github.com/ansible-collections/community.docker.git
synced 2025-12-15 19:42:06 +00:00
Support missing fields and missing types in mounts. (#1134)
This commit is contained in:
parent
8e2056fcb1
commit
fd011d3871
2
changelogs/fragments/1134-docker_container-mounts.yml
Normal file
2
changelogs/fragments/1134-docker_container-mounts.yml
Normal file
@ -0,0 +1,2 @@
|
||||
minor_changes:
|
||||
- "docker_container - support missing fields and new mount types in ``mounts`` (https://github.com/ansible-collections/community.docker/issues/1129, https://github.com/ansible-collections/community.docker/pull/1134)."
|
||||
@ -38,13 +38,19 @@ _DEFAULT_IP_REPLACEMENT_STRING = '[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7
|
||||
|
||||
|
||||
_MOUNT_OPTION_TYPES = dict(
|
||||
volume_driver='volume',
|
||||
volume_options='volume',
|
||||
propagation='bind',
|
||||
no_copy='volume',
|
||||
labels='volume',
|
||||
tmpfs_size='tmpfs',
|
||||
tmpfs_mode='tmpfs',
|
||||
create_mountpoint=('bind',),
|
||||
labels=('volume',),
|
||||
no_copy=('volume',),
|
||||
non_recursive=('bind',),
|
||||
propagation=('bind',),
|
||||
read_only_force_recursive=('bind',),
|
||||
read_only_non_recursive=('bind',),
|
||||
subpath=('volume', 'image'),
|
||||
tmpfs_size=('tmpfs',),
|
||||
tmpfs_mode=('tmpfs',),
|
||||
tmpfs_options=('tmpfs',),
|
||||
volume_driver=('volume',),
|
||||
volume_options=('volume',),
|
||||
)
|
||||
|
||||
|
||||
@ -583,12 +589,18 @@ def _preprocess_mounts(module, values):
|
||||
mount_dict = dict(mount)
|
||||
|
||||
# Sanity checks
|
||||
if mount['source'] is None and mount_type not in ('tmpfs', 'volume'):
|
||||
if mount['source'] is None and mount_type not in ('tmpfs', 'volume', 'image', 'cluster'):
|
||||
module.fail_json(msg='source must be specified for mount "{0}" of type "{1}"'.format(target, mount_type))
|
||||
for option, req_mount_type in _MOUNT_OPTION_TYPES.items():
|
||||
if mount[option] is not None and mount_type != req_mount_type:
|
||||
for option, req_mount_types in _MOUNT_OPTION_TYPES.items():
|
||||
if mount[option] is not None and mount_type not in req_mount_types:
|
||||
module.fail_json(
|
||||
msg='{0} cannot be specified for mount "{1}" of type "{2}" (needs type "{3}")'.format(option, target, mount_type, req_mount_type)
|
||||
msg='{0} cannot be specified for mount "{1}" of type "{2}" (needs type{3} "{4}")'.format(
|
||||
option,
|
||||
target,
|
||||
mount_type,
|
||||
"" if len(req_mount_types) == 1 else "s",
|
||||
'", "'.join(req_mount_types),
|
||||
)
|
||||
)
|
||||
|
||||
# Streamline options
|
||||
@ -607,6 +619,18 @@ def _preprocess_mounts(module, values):
|
||||
mount_dict['tmpfs_mode'] = int(mount_dict['tmpfs_mode'], 8)
|
||||
except Exception as dummy:
|
||||
module.fail_json(msg='tmp_fs mode of mount "{0}" is not an octal string!'.format(target))
|
||||
if mount_dict['tmpfs_options']:
|
||||
opts = []
|
||||
for idx, opt in enumerate(mount_dict['tmpfs_options']):
|
||||
if len(opt) != 1:
|
||||
module.fail_json(msg='tmpfs_options[{1}] of mount "{0}" must be a one-element dictionary!'.format(target, idx + 1))
|
||||
k, v = list(opt.items())[0]
|
||||
if not isinstance(k, str):
|
||||
module.fail_json(msg='key {2!r} in tmpfs_options[{1}] of mount "{0}" must be a string!'.format(target, idx + 1, k))
|
||||
if v is not None and not isinstance(v, str):
|
||||
module.fail_json(msg='value {2!r} in tmpfs_options[{1}] of mount "{0}" must be a string or null/none!'.format(target, idx + 1, v))
|
||||
opts.append([k, v] if v is not None else [k])
|
||||
mount_dict['tmpfs_options'] = opts
|
||||
|
||||
# Add result to list
|
||||
mounts.append(omit_none_from_dict(mount_dict))
|
||||
@ -1169,7 +1193,7 @@ OPTION_MOUNTS_VOLUMES = (
|
||||
.add_option('mounts', type='set', elements='dict', ansible_suboptions=dict(
|
||||
target=dict(type='str', required=True),
|
||||
source=dict(type='str'),
|
||||
type=dict(type='str', choices=['bind', 'volume', 'tmpfs', 'npipe'], default='volume'),
|
||||
type=dict(type='str', choices=['bind', 'volume', 'tmpfs', 'npipe', 'cluster', 'image'], default='volume'),
|
||||
read_only=dict(type='bool'),
|
||||
consistency=dict(type='str', choices=['default', 'consistent', 'cached', 'delegated']),
|
||||
propagation=dict(type='str', choices=['private', 'rprivate', 'shared', 'rshared', 'slave', 'rslave']),
|
||||
@ -1179,6 +1203,12 @@ OPTION_MOUNTS_VOLUMES = (
|
||||
volume_options=dict(type='dict'),
|
||||
tmpfs_size=dict(type='str'),
|
||||
tmpfs_mode=dict(type='str'),
|
||||
non_recursive=dict(type='bool'),
|
||||
create_mountpoint=dict(type='bool'),
|
||||
read_only_non_recursive=dict(type='bool'),
|
||||
read_only_force_recursive=dict(type='bool'),
|
||||
subpath=dict(type='str'),
|
||||
tmpfs_options=dict(type='list', elements='dict'),
|
||||
))
|
||||
.add_option('volumes', type='set', elements='str')
|
||||
.add_option('volume_binds', type='set', elements='str', not_an_ansible_option=True, copy_comparison_from='volumes')
|
||||
|
||||
@ -120,17 +120,6 @@ from ansible_collections.community.docker.plugins.module_utils._api.utils.utils
|
||||
_DEFAULT_IP_REPLACEMENT_STRING = '[[DEFAULT_IP:iewahhaeB4Sae6Aen8IeShairoh4zeph7xaekoh8Geingunaesaeweiy3ooleiwi]]'
|
||||
|
||||
|
||||
_MOUNT_OPTION_TYPES = dict(
|
||||
volume_driver='volume',
|
||||
volume_options='volume',
|
||||
propagation='bind',
|
||||
no_copy='volume',
|
||||
labels='volume',
|
||||
tmpfs_size='tmpfs',
|
||||
tmpfs_mode='tmpfs',
|
||||
)
|
||||
|
||||
|
||||
def _get_ansible_type(type):
|
||||
if type == 'set':
|
||||
return 'list'
|
||||
@ -934,6 +923,12 @@ def _get_values_mounts(module, container, api_version, options, image, host_info
|
||||
'volume_options': mount.get('VolumeOptions', empty_dict).get('DriverConfig', empty_dict).get('Options', empty_dict),
|
||||
'tmpfs_size': mount.get('TmpfsOptions', empty_dict).get('SizeBytes'),
|
||||
'tmpfs_mode': mount.get('TmpfsOptions', empty_dict).get('Mode'),
|
||||
'non_recursive': mount.get('BindOptions', empty_dict).get('NonRecursive'),
|
||||
'create_mountpoint': mount.get('BindOptions', empty_dict).get('CreateMountpoint'),
|
||||
'read_only_non_recursive': mount.get('BindOptions', empty_dict).get('ReadOnlyNonRecursive'),
|
||||
'read_only_force_recursive': mount.get('BindOptions', empty_dict).get('ReadOnlyForceRecursive'),
|
||||
'subpath': mount.get('VolumeOptions', empty_dict).get('Subpath') or mount.get('ImageOptions', empty_dict).get('Subpath'),
|
||||
'tmpfs_options': mount.get('TmpfsOptions', empty_dict).get('Options'),
|
||||
})
|
||||
mounts = result
|
||||
result = {}
|
||||
@ -1026,10 +1021,19 @@ def _set_values_mounts(module, data, api_version, options, values):
|
||||
if 'consistency' in mount:
|
||||
mount_res['Consistency'] = mount['consistency']
|
||||
if mount_type == 'bind':
|
||||
bind_opts = {}
|
||||
if 'propagation' in mount:
|
||||
mount_res['BindOptions'] = {
|
||||
'Propagation': mount['propagation'],
|
||||
}
|
||||
bind_opts['Propagation'] = mount['propagation']
|
||||
if 'non_recursive' in mount:
|
||||
bind_opts['NonRecursive'] = mount['non_recursive']
|
||||
if 'create_mountpoint' in mount:
|
||||
bind_opts['CreateMountpoint'] = mount['create_mountpoint']
|
||||
if 'read_only_non_recursive' in mount:
|
||||
bind_opts['ReadOnlyNonRecursive'] = mount['read_only_non_recursive']
|
||||
if 'read_only_force_recursive' in mount:
|
||||
bind_opts['ReadOnlyForceRecursive'] = mount['read_only_force_recursive']
|
||||
if bind_opts:
|
||||
mount_res['BindOptions'] = bind_opts
|
||||
if mount_type == 'volume':
|
||||
volume_opts = {}
|
||||
if mount.get('no_copy'):
|
||||
@ -1043,6 +1047,8 @@ def _set_values_mounts(module, data, api_version, options, values):
|
||||
if mount.get('volume_options'):
|
||||
driver_config['Options'] = mount.get('volume_options')
|
||||
volume_opts['DriverConfig'] = driver_config
|
||||
if 'subpath' in mount:
|
||||
volume_opts['Subpath'] = mount['subpath']
|
||||
if volume_opts:
|
||||
mount_res['VolumeOptions'] = volume_opts
|
||||
if mount_type == 'tmpfs':
|
||||
@ -1051,8 +1057,16 @@ def _set_values_mounts(module, data, api_version, options, values):
|
||||
tmpfs_opts['Mode'] = mount.get('tmpfs_mode')
|
||||
if mount.get('tmpfs_size'):
|
||||
tmpfs_opts['SizeBytes'] = mount.get('tmpfs_size')
|
||||
if 'tmpfs_options' in mount:
|
||||
tmpfs_opts['Options'] = mount['tmpfs_options']
|
||||
if tmpfs_opts:
|
||||
mount_res['TmpfsOptions'] = tmpfs_opts
|
||||
if mount_type == 'image':
|
||||
image_opts = {}
|
||||
if 'subpath' in mount:
|
||||
image_opts['Subpath'] = mount['subpath']
|
||||
if image_opts:
|
||||
mount_res['ImageOptions'] = image_opts
|
||||
mounts.append(mount_res)
|
||||
data['HostConfig']['Mounts'] = mounts
|
||||
if 'volumes' in values:
|
||||
@ -1486,6 +1500,40 @@ OPTION_MOUNTS_VOLUMES.add_engine('docker_api', DockerAPIEngine(
|
||||
get_value=_get_values_mounts,
|
||||
get_expected_values=_get_expected_values_mounts,
|
||||
set_value=_set_values_mounts,
|
||||
extra_option_minimal_versions={
|
||||
'mounts.non_recursive': {
|
||||
'docker_api_version': '1.40',
|
||||
'detect_usage': lambda c: any(mount.get('non_recursive') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.create_mountpoint': {
|
||||
'docker_api_version': '1.42',
|
||||
'detect_usage': lambda c: any(mount.get('create_mountpoint') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.type=cluster': {
|
||||
'docker_api_version': '1.42',
|
||||
'detect_usage': lambda c: any(mount.get('type') == 'cluster' for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.read_only_non_recursive': {
|
||||
'docker_api_version': '1.44',
|
||||
'detect_usage': lambda c: any(mount.get('read_only_non_recursive') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.read_only_force_recursive': {
|
||||
'docker_api_version': '1.44',
|
||||
'detect_usage': lambda c: any(mount.get('read_only_force_recursive') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.subpath': {
|
||||
'docker_api_version': '1.45',
|
||||
'detect_usage': lambda c: any(mount.get('subpath') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.tmpfs_options': {
|
||||
'docker_api_version': '1.46',
|
||||
'detect_usage': lambda c: any(mount.get('tmpfs_options') is not None for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
'mounts.type=image': {
|
||||
'docker_api_version': '1.47',
|
||||
'detect_usage': lambda c: any(mount.get('type') == 'image' for mount in (c.module.params['mounts'] or [])),
|
||||
},
|
||||
},
|
||||
))
|
||||
|
||||
OPTION_PORTS.add_engine('docker_api', DockerAPIEngine(
|
||||
|
||||
@ -580,12 +580,16 @@ options:
|
||||
description:
|
||||
- The mount type.
|
||||
- Note that V(npipe) is only supported by Docker for Windows.
|
||||
- V(cluster) requires Docker API 1.42+ and has been added in community.docker 4.8.0.
|
||||
- V(image) requires Docker API 1.47+ and has been added in community.docker 4.8.0.
|
||||
type: str
|
||||
choices:
|
||||
- bind
|
||||
- npipe
|
||||
- tmpfs
|
||||
- volume
|
||||
- cluster
|
||||
- image
|
||||
default: volume
|
||||
read_only:
|
||||
description:
|
||||
@ -600,6 +604,13 @@ options:
|
||||
- consistent
|
||||
- default
|
||||
- delegated
|
||||
create_mountpoint:
|
||||
description:
|
||||
- Create mount point on host if missing.
|
||||
- Requires Docker API 1.42+.
|
||||
- Only valid for O(mounts[].type=bind).
|
||||
type: bool
|
||||
version_added: 4.8.0
|
||||
propagation:
|
||||
description:
|
||||
- Propagation mode. Only valid for the V(bind) type.
|
||||
@ -616,6 +627,27 @@ options:
|
||||
- False if the volume should be populated with the data from the target. Only valid for the V(volume) type.
|
||||
- The default value is V(false).
|
||||
type: bool
|
||||
non_recursive:
|
||||
description:
|
||||
- Disable recursive bind mount.
|
||||
- Requires Docker API 1.40+.
|
||||
- Only valid for O(mounts[].type=bind).
|
||||
type: bool
|
||||
version_added: 4.8.0
|
||||
read_only_non_recursive:
|
||||
description:
|
||||
- Make the mount non-recursively read-only, but still leave the mount recursive (unless NonRecursive is set to true in conjunction).
|
||||
- Requires Docker API 1.44+.
|
||||
- Only valid for O(mounts[].type=bind).
|
||||
type: bool
|
||||
version_added: 4.8.0
|
||||
read_only_force_recursive:
|
||||
description:
|
||||
- Raise an error if the mount cannot be made recursively read-only.
|
||||
- Requires Docker API 1.44+.
|
||||
- Only valid for O(mounts[].type=bind).
|
||||
type: bool
|
||||
version_added: 4.8.0
|
||||
labels:
|
||||
description:
|
||||
- User-defined name and labels for the volume. Only valid for the V(volume) type.
|
||||
@ -630,6 +662,13 @@ options:
|
||||
- Dictionary of options specific to the chosen volume_driver. See L(here,https://docs.docker.com/storage/volumes/#use-a-volume-driver)
|
||||
for details.
|
||||
type: dict
|
||||
subpath:
|
||||
type: str
|
||||
description:
|
||||
- Source path inside the volume/image. Must be relative without any back traversals.
|
||||
- Requires Docker API 1.45+.
|
||||
- Only valid for O(mounts[].type=volume) or O(mounts[].type=image).
|
||||
version_added: 4.8.0
|
||||
tmpfs_size:
|
||||
description:
|
||||
- The size for the tmpfs mount in bytes in format <number>[<unit>].
|
||||
@ -641,6 +680,16 @@ options:
|
||||
description:
|
||||
- The permission mode for the tmpfs mount.
|
||||
type: str
|
||||
tmpfs_options:
|
||||
type: list
|
||||
elements: dict
|
||||
description:
|
||||
- Options to be passed to the tmpfs mount.
|
||||
- Every list element must be a dictionary with one key and a value.
|
||||
All keys must be strings, and values can be either a string or V(null)/V(none) for a flag.
|
||||
- Requires Docker API 1.46+.
|
||||
- Only valid for O(mounts[].type=tmpfs).
|
||||
version_added: 4.8.0
|
||||
name:
|
||||
description:
|
||||
- Assign a name to a new container or match an existing container.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user