docker_container: allow more mount modes for volumes (#46598)

* Being more strict about volume mount modes.
This commit is contained in:
Felix Fontein 2018-10-08 11:11:03 +02:00 committed by John R Barker
parent 513be8923d
commit 77127d6768
3 changed files with 43 additions and 12 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- "docker_container - mount modes in ``volumes`` allow more values, similar to when using the ``docker`` executable."

View file

@ -435,9 +435,13 @@ options:
description: description:
- List of volumes to mount within the container. - List of volumes to mount within the container.
- "Use docker CLI-style syntax: C(/host:/container[:mode])" - "Use docker CLI-style syntax: C(/host:/container[:mode])"
- You can specify a read mode for the mount with either C(ro) or C(rw). - "Mount modes can be a comma-separated list of various modes such as C(ro), C(rw), C(consistent),
C(delegated), C(cached), C(rprivate), C(private), C(rshared), C(shared), C(rslave), C(slave).
Note that docker might not support all modes and combinations of such modes."
- SELinux hosts can additionally use C(z) or C(Z) to use a shared or - SELinux hosts can additionally use C(z) or C(Z) to use a shared or
private label for the volume. private label for the volume.
- "Note that Ansible 2.7 and earlier only supported one mode, which had to be one of C(ro), C(rw),
C(z), and C(Z)."
volume_driver: volume_driver:
description: description:
- The container volume driver. - The container volume driver.
@ -724,7 +728,12 @@ REQUIRES_CONVERSION_TO_BYTES = [
'shm_size' 'shm_size'
] ]
VOLUME_PERMISSIONS = ('rw', 'ro', 'z', 'Z')
def is_volume_permissions(input):
for part in input.split(','):
if part not in ('rw', 'ro', 'z', 'Z', 'consistent', 'delegated', 'cached', 'rprivate', 'private', 'rshared', 'shared', 'rslave', 'slave'):
return False
return True
class TaskParameters(DockerBaseClass): class TaskParameters(DockerBaseClass):
@ -964,13 +973,15 @@ class TaskParameters(DockerBaseClass):
if ':' in vol: if ':' in vol:
if len(vol.split(':')) == 3: if len(vol.split(':')) == 3:
host, container, mode = vol.split(':') host, container, mode = vol.split(':')
if not is_volume_permissions(mode):
self.fail('Found invalid volumes mode: {0}'.format(mode))
if re.match(r'[.~]', host): if re.match(r'[.~]', host):
host = os.path.abspath(os.path.expanduser(host)) host = os.path.abspath(os.path.expanduser(host))
new_vols.append("%s:%s:%s" % (host, container, mode)) new_vols.append("%s:%s:%s" % (host, container, mode))
continue continue
elif len(vol.split(':')) == 2: elif len(vol.split(':')) == 2:
parts = vol.split(':') parts = vol.split(':')
if parts[1] not in VOLUME_PERMISSIONS and re.match(r'[.~]', parts[0]): if not is_volume_permissions(parts[1]) and re.match(r'[.~]', parts[0]):
host = os.path.abspath(os.path.expanduser(parts[0])) host = os.path.abspath(os.path.expanduser(parts[0]))
new_vols.append("%s:%s:rw" % (host, parts[1])) new_vols.append("%s:%s:rw" % (host, parts[1]))
continue continue
@ -992,7 +1003,7 @@ class TaskParameters(DockerBaseClass):
continue continue
if len(vol.split(':')) == 2: if len(vol.split(':')) == 2:
parts = vol.split(':') parts = vol.split(':')
if parts[1] not in VOLUME_PERMISSIONS: if not is_volume_permissions(parts[1]):
result.append(parts[1]) result.append(parts[1])
continue continue
result.append(vol) result.append(vol)
@ -1119,8 +1130,7 @@ class TaskParameters(DockerBaseClass):
binds[container_port] = bind binds[container_port] = bind
return binds return binds
@staticmethod def _get_volume_binds(self, volumes):
def _get_volume_binds(volumes):
''' '''
Extract host bindings, if any, from list of volume mapping strings. Extract host bindings, if any, from list of volume mapping strings.
@ -1133,9 +1143,11 @@ class TaskParameters(DockerBaseClass):
if ':' in vol: if ':' in vol:
if len(vol.split(':')) == 3: if len(vol.split(':')) == 3:
host, container, mode = vol.split(':') host, container, mode = vol.split(':')
if not is_volume_permissions(mode):
self.fail('Found invalid volumes mode: {0}'.format(mode))
if len(vol.split(':')) == 2: if len(vol.split(':')) == 2:
parts = vol.split(':') parts = vol.split(':')
if parts[1] not in VOLUME_PERMISSIONS: if not is_volume_permissions(parts[1]):
host, container, mode = (vol.split(':') + ['rw']) host, container, mode = (vol.split(':') + ['rw'])
if host is not None: if host is not None:
result[host] = dict( result[host] = dict(
@ -1747,9 +1759,11 @@ class Container(DockerBaseClass):
if ':' in vol: if ':' in vol:
if len(vol.split(':')) == 3: if len(vol.split(':')) == 3:
host, container, mode = vol.split(':') host, container, mode = vol.split(':')
if not is_volume_permissions(mode):
self.fail('Found invalid volumes mode: {0}'.format(mode))
if len(vol.split(':')) == 2: if len(vol.split(':')) == 2:
parts = vol.split(':') parts = vol.split(':')
if parts[1] not in VOLUME_PERMISSIONS: if not is_volume_permissions(parts[1]):
host, container, mode = vol.split(':') + ['rw'] host, container, mode = vol.split(':') + ['rw']
if host: if host:
param_vols.append("%s:%s:%s" % (host, container, mode)) param_vols.append("%s:%s:%s" % (host, container, mode))
@ -1796,9 +1810,11 @@ class Container(DockerBaseClass):
if ':' in vol: if ':' in vol:
if len(vol.split(':')) == 3: if len(vol.split(':')) == 3:
host, container, mode = vol.split(':') host, container, mode = vol.split(':')
if not is_volume_permissions(mode):
self.fail('Found invalid volumes mode: {0}'.format(mode))
if len(vol.split(':')) == 2: if len(vol.split(':')) == 2:
parts = vol.split(':') parts = vol.split(':')
if parts[1] not in VOLUME_PERMISSIONS: if not is_volume_permissions(parts[1]):
host, container, mode = vol.split(':') + ['rw'] host, container, mode = vol.split(':') + ['rw']
new_vol = dict() new_vol = dict()
if container: if container:

View file

@ -3109,7 +3109,7 @@
state: started state: started
volumes: volumes:
- "/tmp:/tmp" - "/tmp:/tmp"
- "/:/whatever" - "/:/whatever:rw,z"
register: volumes_1 register: volumes_1
- name: volumes (idempotency) - name: volumes (idempotency)
@ -3119,7 +3119,7 @@
name: "{{ cname }}" name: "{{ cname }}"
state: started state: started
volumes: volumes:
- "/:/whatever" - "/:/whatever:rw,z"
- "/tmp:/tmp" - "/tmp:/tmp"
register: volumes_2 register: volumes_2
@ -3141,10 +3141,22 @@
state: started state: started
volumes: volumes:
- "/tmp:/tmp" - "/tmp:/tmp"
- "/tmp:/somewhereelse:ro" - "/tmp:/somewhereelse:ro,Z"
stop_timeout: 1 stop_timeout: 1
register: volumes_4 register: volumes_4
- name: volumes (different modes)
docker_container:
image: alpine:3.8
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/tmp:/tmp"
- "/tmp:/somewhereelse:ro"
stop_timeout: 1
register: volumes_5
- name: cleanup - name: cleanup
docker_container: docker_container:
name: "{{ cname }}" name: "{{ cname }}"
@ -3157,6 +3169,7 @@
- volumes_2 is not changed - volumes_2 is not changed
- volumes_3 is not changed - volumes_3 is not changed
- volumes_4 is changed - volumes_4 is changed
- volumes_5 is changed
#################################################################### ####################################################################
## volumes_from #################################################### ## volumes_from ####################################################