diff --git a/compose/config.py b/compose/config.py index cfa5ce44a..668d2b726 100644 --- a/compose/config.py +++ b/compose/config.py @@ -166,6 +166,11 @@ def process_container_options(service_dict, working_dir=None): msg += " (did you mean '%s'?)" % DOCKER_CONFIG_HINTS[k] raise ConfigurationError(msg) + service_dict = service_dict.copy() + + if 'volumes' in service_dict: + service_dict['volumes'] = resolve_host_paths(service_dict['volumes'], working_dir=working_dir) + return service_dict @@ -178,8 +183,14 @@ def merge_service_dicts(base, override): override.get('environment'), ) + if 'volumes' in base or 'volumes' in override: + d['volumes'] = merge_volumes( + base.get('volumes'), + override.get('volumes'), + ) + for k in ALLOWED_KEYS: - if k not in ['environment']: + if k not in ['environment', 'volumes']: if k in override: d[k] = override[k] @@ -285,6 +296,42 @@ def env_vars_from_file(filename): return env +def resolve_host_paths(volumes, working_dir=None): + if working_dir is None: + raise Exception("No working_dir passed to resolve_host_paths()") + + return [resolve_host_path(v, working_dir) for v in volumes] + + +def resolve_host_path(volume, working_dir): + container_path, host_path = split_volume(volume) + if host_path is not None: + return "%s:%s" % (expand_path(working_dir, host_path), container_path) + else: + return container_path + + +def merge_volumes(base, override): + d = dict_from_volumes(base) + d.update(dict_from_volumes(override)) + return volumes_from_dict(d) + + +def dict_from_volumes(volumes): + return dict(split_volume(v) for v in volumes) + + +def split_volume(volume): + if ':' in volume: + return reversed(volume.split(':', 1)) + else: + return (volume, None) + + +def volumes_from_dict(d): + return ["%s:%s" % (host_path, container_path) for (container_path, host_path) in d.items()] + + def expand_path(working_dir, path): return os.path.abspath(os.path.join(working_dir, path)) diff --git a/tests/fixtures/volume-path/common/services.yml b/tests/fixtures/volume-path/common/services.yml new file mode 100644 index 000000000..2dbf75961 --- /dev/null +++ b/tests/fixtures/volume-path/common/services.yml @@ -0,0 +1,5 @@ +db: + image: busybox + volumes: + - ./foo:/foo + - ./bar:/bar diff --git a/tests/fixtures/volume-path/docker-compose.yml b/tests/fixtures/volume-path/docker-compose.yml new file mode 100644 index 000000000..af433c52f --- /dev/null +++ b/tests/fixtures/volume-path/docker-compose.yml @@ -0,0 +1,6 @@ +db: + extends: + file: common/services.yml + service: db + volumes: + - ./bar:/bar diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index 3212b0be9..a585740f4 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -7,18 +7,19 @@ from .testcases import DockerClientTestCase class ProjectTest(DockerClientTestCase): def test_volumes_from_service(self): + service_dicts = config.from_dictionary({ + 'data': { + 'image': 'busybox:latest', + 'volumes': ['/var/data'], + }, + 'db': { + 'image': 'busybox:latest', + 'volumes_from': ['data'], + }, + }, working_dir='.') project = Project.from_dicts( name='composetest', - service_dicts=config.from_dictionary({ - 'data': { - 'image': 'busybox:latest', - 'volumes': ['/var/data'], - }, - 'db': { - 'image': 'busybox:latest', - 'volumes_from': ['data'], - }, - }), + service_dicts=service_dicts, client=self.client, ) db = project.get_service('db') diff --git a/tests/unit/config_test.py b/tests/unit/config_test.py index 7e18e2e92..013ad5031 100644 --- a/tests/unit/config_test.py +++ b/tests/unit/config_test.py @@ -133,7 +133,6 @@ class EnvTest(unittest.TestCase): {'FILE_DEF': 'F1', 'FILE_DEF_EMPTY': '', 'ENV_DEF': 'E3', 'NO_DEF': ''}, ) - class ExtendsTest(unittest.TestCase): def test_extends(self): service_dicts = config.load('tests/fixtures/extends/docker-compose.yml') @@ -241,3 +240,13 @@ class ExtendsTest(unittest.TestCase): with mock.patch.object(config, 'load_yaml', return_value=other_config): print load_config() + + def test_volume_path(self): + dicts = config.load('tests/fixtures/volume-path/docker-compose.yml') + + paths = [ + '%s:/foo' % os.path.abspath('tests/fixtures/volume-path/common/foo'), + '%s:/bar' % os.path.abspath('tests/fixtures/volume-path/bar'), + ] + + self.assertEqual(set(dicts[0]['volumes']), set(paths))