Browse Source

volume path compatibility with engine

An expanded windows path of c:\shiny\thing:\shiny:rw
needs to be changed to be linux style path, including the drive,
like /c/shiny/thing /shiny
to be mounted successfully by the engine.

Signed-off-by: Mazz Mosley <[email protected]>
Mazz Mosley 10 years ago
parent
commit
2276326d7e
3 changed files with 38 additions and 19 deletions
  1. 34 13
      compose/service.py
  2. 1 2
      tests/unit/config/config_test.py
  3. 3 4
      tests/unit/service_test.py

+ 34 - 13
compose/service.py

@@ -937,33 +937,54 @@ def build_volume_binding(volume_spec):
     return volume_spec.internal, "{}:{}:{}".format(*volume_spec)
 
 
-def parse_volume_spec(volume_config):
+def normalize_paths_for_engine(external_path, internal_path):
     """
-    A volume_config string, which is a path, split it into external:internal[:mode]
-    parts to be returned as a valid VolumeSpec tuple.
+    Windows paths, c:\my\path\shiny, need to be changed to be compatible with
+    the Engine. Volume paths are expected to be linux style /c/my/path/shiny/
     """
-    parts = volume_config.split(':')
+    if IS_WINDOWS_PLATFORM:
+        if external_path:
+            drive, tail = os.path.splitdrive(external_path)
+
+            if drive:
+                reformatted_drive = "/{}".format(drive.replace(":", ""))
+                external_path = reformatted_drive + tail
+
+            external_path = "/".join(external_path.split("\\"))
+
+        return external_path, "/".join(internal_path.split("\\"))
+    else:
+        return external_path, internal_path
 
+
+def parse_volume_spec(volume_config):
+    """
+    Parse a volume_config path and split it into external:internal[:mode]
+    parts to be returned as a valid VolumeSpec.
+    """
     if IS_WINDOWS_PLATFORM:
         # relative paths in windows expand to include the drive, eg C:\
         # so we join the first 2 parts back together to count as one
-        drive, volume_path = os.path.splitdrive(volume_config)
-        windows_parts = volume_path.split(":")
-        windows_parts[0] = os.path.join(drive, windows_parts[0])
-        parts = windows_parts
+        drive, tail = os.path.splitdrive(volume_config)
+        parts = tail.split(":")
+
+        if drive:
+            parts[0] = drive + parts[0]
+    else:
+        parts = volume_config.split(':')
 
     if len(parts) > 3:
         raise ConfigError("Volume %s has incorrect format, should be "
                           "external:internal[:mode]" % volume_config)
 
     if len(parts) == 1:
-        external = None
-        internal = os.path.normpath(parts[0])
+        external, internal = normalize_paths_for_engine(None, os.path.normpath(parts[0]))
     else:
-        external = os.path.normpath(parts[0])
-        internal = os.path.normpath(parts[1])
+        external, internal = normalize_paths_for_engine(os.path.normpath(parts[0]), os.path.normpath(parts[1]))
 
-    mode = parts[2] if len(parts) == 3 else 'rw'
+    mode = 'rw'
+    if len(parts) == 3:
+        mode = parts[2]
 
     return VolumeSpec(external, internal, mode)
 

+ 1 - 2
tests/unit/config/config_test.py

@@ -420,7 +420,6 @@ class VolumeConfigTest(unittest.TestCase):
         d = make_service_dict('foo', {'build': '.', 'volumes': ['/data']}, working_dir='.')
         self.assertEqual(d['volumes'], ['/data'])
 
-    @pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
     @mock.patch.dict(os.environ)
     def test_volume_binding_with_environment_variable(self):
         os.environ['VOLUME_PATH'] = '/host/path'
@@ -433,7 +432,7 @@ class VolumeConfigTest(unittest.TestCase):
         )[0]
         self.assertEqual(d['volumes'], ['/host/path:/container/path'])
 
-    @pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
+    @pytest.mark.skipif(IS_WINDOWS_PLATFORM, reason='posix paths')
     @mock.patch.dict(os.environ)
     def test_volume_binding_with_home(self):
         os.environ['HOME'] = '/home/user'

+ 3 - 4
tests/unit/service_test.py

@@ -441,7 +441,6 @@ def mock_get_image(images):
         raise NoSuchImageError()
 
 
[email protected](IS_WINDOWS_PLATFORM, reason='paths use slash')
 class ServiceVolumesTest(unittest.TestCase):
 
     def setUp(self):
@@ -468,15 +467,15 @@ class ServiceVolumesTest(unittest.TestCase):
 
     @pytest.mark.xfail((not IS_WINDOWS_PLATFORM), reason='does not have a drive')
     def test_parse_volume_windows_relative_path(self):
-        windows_relative_path = "c:\\Users\\msamblanet\\Documents\\anvil\\connect\\config:\\opt\\connect\\config:ro"
+        windows_relative_path = "c:\\Users\\me\\Documents\\shiny\\config:\\opt\\shiny\\config:ro"
 
         spec = parse_volume_spec(windows_relative_path)
 
         self.assertEqual(
             spec,
             (
-                "c:\\Users\\msamblanet\\Documents\\anvil\\connect\\config",
-                "\\opt\\connect\\config",
+                "/c/Users/me/Documents/shiny/config",
+                "/opt/shiny/config",
                 "ro"
             )
         )