فهرست منبع

Make volume host paths relative to file, merge volumes when extending

Signed-off-by: Aanand Prasad <[email protected]>
Aanand Prasad 10 سال پیش
والد
کامیت
37efdb1f8b

+ 48 - 1
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))
 

+ 5 - 0
tests/fixtures/volume-path/common/services.yml

@@ -0,0 +1,5 @@
+db:
+  image: busybox
+  volumes:
+    - ./foo:/foo
+    - ./bar:/bar

+ 6 - 0
tests/fixtures/volume-path/docker-compose.yml

@@ -0,0 +1,6 @@
+db:
+  extends:
+    file: common/services.yml
+    service: db
+  volumes:
+    - ./bar:/bar

+ 11 - 10
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')

+ 10 - 1
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))