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 <mazz@houseofmnowster.com>
This commit is contained in:
Mazz Mosley 2015-10-01 11:06:15 +01:00
parent 58270d8859
commit 2276326d7e
3 changed files with 38 additions and 19 deletions

View File

@ -937,33 +937,54 @@ def build_volume_binding(volume_spec):
return volume_spec.internal, "{}:{}:{}".format(*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): def parse_volume_spec(volume_config):
""" """
A volume_config string, which is a path, split it into external:internal[:mode] Parse a volume_config path and split it into external:internal[:mode]
parts to be returned as a valid VolumeSpec tuple. parts to be returned as a valid VolumeSpec.
""" """
parts = volume_config.split(':')
if IS_WINDOWS_PLATFORM: if IS_WINDOWS_PLATFORM:
# relative paths in windows expand to include the drive, eg C:\ # relative paths in windows expand to include the drive, eg C:\
# so we join the first 2 parts back together to count as one # so we join the first 2 parts back together to count as one
drive, volume_path = os.path.splitdrive(volume_config) drive, tail = os.path.splitdrive(volume_config)
windows_parts = volume_path.split(":") parts = tail.split(":")
windows_parts[0] = os.path.join(drive, windows_parts[0])
parts = windows_parts if drive:
parts[0] = drive + parts[0]
else:
parts = volume_config.split(':')
if len(parts) > 3: if len(parts) > 3:
raise ConfigError("Volume %s has incorrect format, should be " raise ConfigError("Volume %s has incorrect format, should be "
"external:internal[:mode]" % volume_config) "external:internal[:mode]" % volume_config)
if len(parts) == 1: if len(parts) == 1:
external = None external, internal = normalize_paths_for_engine(None, os.path.normpath(parts[0]))
internal = os.path.normpath(parts[0])
else: else:
external = os.path.normpath(parts[0]) external, internal = normalize_paths_for_engine(os.path.normpath(parts[0]), os.path.normpath(parts[1]))
internal = 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) return VolumeSpec(external, internal, mode)

View File

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

View File

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