Handle windows volume paths

When a relative path is expanded and we're on a windows platform,
it expands to include the drive, eg C:\ , which was causing a ConfigError
as we split on ":" in parse_volume_spec and that was giving too many parts.

Use os.path.splitdrive instead of manually calculating the drive.

This should help us deal with windows drives as part of the volume
path better than us doing it manually.

Signed-off-by: Mazz Mosley <mazz@houseofmnowster.com>
This commit is contained in:
Mazz Mosley 2015-09-22 16:13:42 +01:00
parent c673ce37c7
commit f4cd5b1d45
5 changed files with 51 additions and 5 deletions

View File

@ -526,12 +526,13 @@ def path_mappings_from_dict(d):
return [join_path_mapping(v) for v in d.items()]
def split_path_mapping(string):
if ':' in string:
(host, container) = string.split(':', 1)
return (container, host)
def split_path_mapping(volume_path):
drive, volume_config = os.path.splitdrive(volume_path)
if ':' in volume_config:
(host, container) = volume_config.split(':', 1)
return (container, drive + host)
else:
return (string, None)
return (volume_path, None)
def join_path_mapping(pair):

View File

@ -2,6 +2,7 @@ import os
import sys
DEFAULT_TIMEOUT = 10
IS_WINDOWS_PLATFORM = (sys.platform == "win32")
LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
LABEL_ONE_OFF = 'com.docker.compose.oneoff'
LABEL_PROJECT = 'com.docker.compose.project'

View File

@ -20,6 +20,7 @@ from .config import DOCKER_CONFIG_KEYS
from .config import merge_environment
from .config.validation import VALID_NAME_CHARS
from .const import DEFAULT_TIMEOUT
from .const import IS_WINDOWS_PLATFORM
from .const import LABEL_CONFIG_HASH
from .const import LABEL_CONTAINER_NUMBER
from .const import LABEL_ONE_OFF
@ -937,7 +938,20 @@ def build_volume_binding(volume_spec):
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.
"""
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
if len(parts) > 3:
raise ConfigError("Volume %s has incorrect format, should be "
"external:internal[:mode]" % volume_config)

View File

@ -1124,6 +1124,21 @@ class ExpandPathTest(unittest.TestCase):
self.assertEqual(result, user_path + 'otherdir/somefile')
class VolumePathTest(unittest.TestCase):
@pytest.mark.xfail((not IS_WINDOWS_PLATFORM), reason='does not have a drive')
def test_split_path_mapping_with_windows_path(self):
windows_volume_path = "c:\\Users\\msamblanet\\Documents\\anvil\\connect\\config:/opt/connect/config:ro"
expected_mapping = (
"/opt/connect/config:ro",
"c:\\Users\\msamblanet\\Documents\\anvil\\connect\\config"
)
mapping = config.split_path_mapping(windows_volume_path)
self.assertEqual(mapping, expected_mapping)
@pytest.mark.xfail(IS_WINDOWS_PLATFORM, reason='paths use slash')
class BuildPathTest(unittest.TestCase):
def setUp(self):

View File

@ -466,6 +466,21 @@ class ServiceVolumesTest(unittest.TestCase):
with self.assertRaises(ConfigError):
parse_volume_spec('one:two:three:four')
@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"
spec = parse_volume_spec(windows_relative_path)
self.assertEqual(
spec,
(
"c:\\Users\\msamblanet\\Documents\\anvil\\connect\\config",
"\\opt\\connect\\config",
"ro"
)
)
def test_build_volume_binding(self):
binding = build_volume_binding(parse_volume_spec('/outside:/inside'))
self.assertEqual(binding, ('/inside', '/outside:/inside:rw'))