diff --git a/compose/service.py b/compose/service.py index 4df10fbb1..c9ca00ae4 100644 --- a/compose/service.py +++ b/compose/service.py @@ -937,33 +937,54 @@ def build_volume_binding(volume_spec): return volume_spec.internal, "{}:{}:{}".format(*volume_spec) +def normalize_paths_for_engine(external_path, internal_path): + """ + 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/ + """ + 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): """ - A volume_config string, which is a path, split it into external:internal[:mode] - parts to be returned as a valid VolumeSpec tuple. + Parse a volume_config path and split it into external:internal[:mode] + parts to be returned as a valid VolumeSpec. """ - parts = volume_config.split(':') - 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) diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index cf299738f..c06a2a327 100644 --- a/tests/unit/config/config_test.py +++ b/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' diff --git a/tests/unit/service_test.py b/tests/unit/service_test.py index b0cee1ee1..a3db0d434 100644 --- a/tests/unit/service_test.py +++ b/tests/unit/service_test.py @@ -441,7 +441,6 @@ def mock_get_image(images): raise NoSuchImageError() -@pytest.mark.xfail(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" ) )