|
@@ -12,6 +12,7 @@ import six
|
|
|
from compose.config.config import V1
|
|
|
from compose.config.errors import ConfigurationError
|
|
|
from compose.const import IS_WINDOWS_PLATFORM
|
|
|
+from compose.utils import splitdrive
|
|
|
|
|
|
|
|
|
class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode type')):
|
|
@@ -114,41 +115,23 @@ def parse_extra_hosts(extra_hosts_config):
|
|
|
return extra_hosts_dict
|
|
|
|
|
|
|
|
|
-def normalize_paths_for_engine(external_path, internal_path):
|
|
|
+def normalize_path_for_engine(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 not IS_WINDOWS_PLATFORM:
|
|
|
- return external_path, internal_path
|
|
|
+ drive, tail = splitdrive(path)
|
|
|
|
|
|
- if external_path:
|
|
|
- drive, tail = os.path.splitdrive(external_path)
|
|
|
+ if drive:
|
|
|
+ path = '/' + drive.lower().rstrip(':') + tail
|
|
|
|
|
|
- if drive:
|
|
|
- external_path = '/' + drive.lower().rstrip(':') + tail
|
|
|
-
|
|
|
- external_path = external_path.replace('\\', '/')
|
|
|
-
|
|
|
- return external_path, internal_path.replace('\\', '/')
|
|
|
+ return path.replace('\\', '/')
|
|
|
|
|
|
|
|
|
class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
|
|
|
|
|
|
@classmethod
|
|
|
- def parse(cls, 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, tail = os.path.splitdrive(volume_config)
|
|
|
- parts = tail.split(":")
|
|
|
-
|
|
|
- if drive:
|
|
|
- parts[0] = drive + parts[0]
|
|
|
- else:
|
|
|
- parts = volume_config.split(':')
|
|
|
+ def _parse_unix(cls, volume_config):
|
|
|
+ parts = volume_config.split(':')
|
|
|
|
|
|
if len(parts) > 3:
|
|
|
raise ConfigurationError(
|
|
@@ -156,13 +139,11 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
|
|
|
"external:internal[:mode]" % volume_config)
|
|
|
|
|
|
if len(parts) == 1:
|
|
|
- external, internal = normalize_paths_for_engine(
|
|
|
- None,
|
|
|
- os.path.normpath(parts[0]))
|
|
|
+ external = None
|
|
|
+ internal = os.path.normpath(parts[0])
|
|
|
else:
|
|
|
- external, internal = normalize_paths_for_engine(
|
|
|
- os.path.normpath(parts[0]),
|
|
|
- os.path.normpath(parts[1]))
|
|
|
+ external = os.path.normpath(parts[0])
|
|
|
+ internal = os.path.normpath(parts[1])
|
|
|
|
|
|
mode = 'rw'
|
|
|
if len(parts) == 3:
|
|
@@ -170,6 +151,48 @@ class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
|
|
|
|
|
|
return cls(external, internal, mode)
|
|
|
|
|
|
+ @classmethod
|
|
|
+ def _parse_win32(cls, volume_config):
|
|
|
+ # relative paths in windows expand to include the drive, eg C:\
|
|
|
+ # so we join the first 2 parts back together to count as one
|
|
|
+ mode = 'rw'
|
|
|
+
|
|
|
+ def separate_next_section(volume_config):
|
|
|
+ drive, tail = splitdrive(volume_config)
|
|
|
+ parts = tail.split(':', 1)
|
|
|
+ if drive:
|
|
|
+ parts[0] = drive + parts[0]
|
|
|
+ return parts
|
|
|
+
|
|
|
+ parts = separate_next_section(volume_config)
|
|
|
+ if len(parts) == 1:
|
|
|
+ internal = normalize_path_for_engine(os.path.normpath(parts[0]))
|
|
|
+ external = None
|
|
|
+ else:
|
|
|
+ external = parts[0]
|
|
|
+ parts = separate_next_section(parts[1])
|
|
|
+ external = normalize_path_for_engine(os.path.normpath(external))
|
|
|
+ internal = normalize_path_for_engine(os.path.normpath(parts[0]))
|
|
|
+ if len(parts) > 1:
|
|
|
+ if ':' in parts[1]:
|
|
|
+ raise ConfigurationError(
|
|
|
+ "Volume %s has incorrect format, should be "
|
|
|
+ "external:internal[:mode]" % volume_config
|
|
|
+ )
|
|
|
+ mode = parts[1]
|
|
|
+
|
|
|
+ return cls(external, internal, mode)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def parse(cls, 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:
|
|
|
+ return cls._parse_win32(volume_config)
|
|
|
+ else:
|
|
|
+ return cls._parse_unix(volume_config)
|
|
|
+
|
|
|
def repr(self):
|
|
|
external = self.external + ':' if self.external else ''
|
|
|
return '{ext}{v.internal}:{v.mode}'.format(ext=external, v=self)
|