diff --git a/docker/models/containers.py b/docker/models/containers.py index 9c9e92c90f..56d0081c53 100644 --- a/docker/models/containers.py +++ b/docker/models/containers.py @@ -1187,10 +1187,10 @@ def _host_volume_from_bind(bind): bits = rest.split(':', 1) if len(bits) == 1 or bits[1] in ('ro', 'rw'): return drive + bits[0] - elif bits[1].endswith(':ro') or bits[1].endswith(':rw'): - return bits[1][:-3] - else: - return bits[1] + # bits[1] is "dest" or "dest:mode", where mode can be any bind option + # ("ro", "z", "ro,Z", "rw,rprivate", "cached", ...) + dest_drive, dest = ntpath.splitdrive(bits[1]) + return dest_drive + dest.split(':', 1)[0] ExecResult = namedtuple('ExecResult', 'exit_code,output') diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py index 0e2ae341a9..9c2cc27aa3 100644 --- a/tests/unit/models_containers_test.py +++ b/tests/unit/models_containers_test.py @@ -484,6 +484,36 @@ def test_create(self): ) client.api.inspect_container.assert_called_with(FAKE_CONTAINER_ID) + def test_create_volumes_with_bind_options(self): + # Mode options other than a bare "ro"/"rw" (SELinux labels, + # propagation, macOS consistency, ...) must not leak into the + # container's anonymous volume list. + binds = [ + '/tmp:/mnt/z:z', + '/tmp:/mnt/ro-z:ro,Z', + '/tmp:/mnt/rprivate:rw,rprivate', + 'src:/mnt/cached:cached', + 'volumename:/mnt/nocopy:nocopy', + '/tmp:/mnt/ro:ro', + 'C:\\windows\\path:D:\\hello\\world:ro,Z', + ] + client = make_fake_client() + client.containers.create('alpine', volumes=binds) + client.api.create_container.assert_called_with( + image='alpine', + command=None, + volumes=[ + '/mnt/z', + '/mnt/ro-z', + '/mnt/rprivate', + '/mnt/cached', + '/mnt/nocopy', + '/mnt/ro', + 'D:\\hello\\world', + ], + host_config={'Binds': binds, 'NetworkMode': 'default'}, + ) + def test_create_with_image_object(self): client = make_fake_client() image = client.images.get(FAKE_IMAGE_ID)