From d980d170a6a58df573c0b78211fd35a9d33286a2 Mon Sep 17 00:00:00 2001 From: Collins Abitekaniza Date: Sat, 8 Dec 2018 13:48:05 +0300 Subject: [PATCH 1/2] error on duplicate mount points Signed-off-by: Collins Abitekaniza --- compose/config/config.py | 12 ++++++++++++ compose/service.py | 1 + 2 files changed, 13 insertions(+) diff --git a/compose/config/config.py b/compose/config/config.py index 0298b4e2d..59c76680b 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -8,6 +8,7 @@ import os import string import sys from collections import namedtuple +from operator import itemgetter, attrgetter import six import yaml @@ -835,6 +836,17 @@ def finalize_service_volumes(service_dict, environment): finalized_volumes.append(MountSpec.parse(v, normalize, win_host)) else: finalized_volumes.append(VolumeSpec.parse(v, normalize, win_host)) + + duplicate_mounts = [] + mounts = [v.as_volume_spec() if isinstance(v, MountSpec) else v for v in finalized_volumes] + for mount in mounts: + if list(map(attrgetter('internal'), mounts)).count(mount.internal) > 1: + duplicate_mounts.append(mount.repr()) + + if duplicate_mounts: + raise ConfigurationError("Duplicate mount points: volumes [%s]" % ( + ', '.join(duplicate_mounts))) + service_dict['volumes'] = finalized_volumes return service_dict diff --git a/compose/service.py b/compose/service.py index f6dfa7c72..964ab0193 100644 --- a/compose/service.py +++ b/compose/service.py @@ -9,6 +9,7 @@ import sys from collections import namedtuple from collections import OrderedDict from operator import attrgetter +from operator import itemgetter import enum import six From 47ff8d710c62a2c58e359ac41a90d7b6c411de90 Mon Sep 17 00:00:00 2001 From: Collins Abitekaniza Date: Sun, 9 Dec 2018 00:43:06 +0300 Subject: [PATCH 2/2] test create from config with duplicate mount points Signed-off-by: Collins Abitekaniza --- compose/config/config.py | 6 +++--- compose/service.py | 1 - tests/unit/config/config_test.py | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/compose/config/config.py b/compose/config/config.py index 59c76680b..d9ee158d5 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -8,7 +8,7 @@ import os import string import sys from collections import namedtuple -from operator import itemgetter, attrgetter +from operator import attrgetter import six import yaml @@ -844,9 +844,9 @@ def finalize_service_volumes(service_dict, environment): duplicate_mounts.append(mount.repr()) if duplicate_mounts: - raise ConfigurationError("Duplicate mount points: volumes [%s]" % ( + raise ConfigurationError("Duplicate mount points: [%s]" % ( ', '.join(duplicate_mounts))) - + service_dict['volumes'] = finalized_volumes return service_dict diff --git a/compose/service.py b/compose/service.py index 964ab0193..f6dfa7c72 100644 --- a/compose/service.py +++ b/compose/service.py @@ -9,7 +9,6 @@ import sys from collections import namedtuple from collections import OrderedDict from operator import attrgetter -from operator import itemgetter import enum import six diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index 787d8ff4a..f95c46d89 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -3071,6 +3071,41 @@ class ConfigTest(unittest.TestCase): ) config.load(config_details) + def test_config_duplicate_mount_points(self): + config1 = build_config_details( + { + 'version': '3.5', + 'services': { + 'web': { + 'image': 'busybox', + 'volumes': ['/tmp/foo:/tmp/foo', '/tmp/foo:/tmp/foo:rw'] + } + } + } + ) + + config2 = build_config_details( + { + 'version': '3.5', + 'services': { + 'web': { + 'image': 'busybox', + 'volumes': ['/x:/y', '/z:/y'] + } + } + } + ) + + with self.assertRaises(ConfigurationError) as e: + config.load(config1) + self.assertEquals(str(e.exception), 'Duplicate mount points: [%s]' % ( + ', '.join(['/tmp/foo:/tmp/foo:rw']*2))) + + with self.assertRaises(ConfigurationError) as e: + config.load(config2) + self.assertEquals(str(e.exception), 'Duplicate mount points: [%s]' % ( + ', '.join(['/x:/y:rw', '/z:/y:rw']))) + class NetworkModeTest(unittest.TestCase):