types.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. """
  2. Types for objects parsed from the configuration.
  3. """
  4. from __future__ import absolute_import
  5. from __future__ import unicode_literals
  6. import os
  7. from collections import namedtuple
  8. from compose.config.errors import ConfigurationError
  9. from compose.const import IS_WINDOWS_PLATFORM
  10. class VolumeFromSpec(namedtuple('_VolumeFromSpec', 'source mode')):
  11. @classmethod
  12. def parse(cls, volume_from_config):
  13. parts = volume_from_config.split(':')
  14. if len(parts) > 2:
  15. raise ConfigurationError(
  16. "volume_from {} has incorrect format, should be "
  17. "service[:mode]".format(volume_from_config))
  18. if len(parts) == 1:
  19. source = parts[0]
  20. mode = 'rw'
  21. else:
  22. source, mode = parts
  23. return cls(source, mode)
  24. def parse_restart_spec(restart_config):
  25. if not restart_config:
  26. return None
  27. parts = restart_config.split(':')
  28. if len(parts) > 2:
  29. raise ConfigurationError(
  30. "Restart %s has incorrect format, should be "
  31. "mode[:max_retry]" % restart_config)
  32. if len(parts) == 2:
  33. name, max_retry_count = parts
  34. else:
  35. name, = parts
  36. max_retry_count = 0
  37. return {'Name': name, 'MaximumRetryCount': int(max_retry_count)}
  38. def parse_extra_hosts(extra_hosts_config):
  39. if not extra_hosts_config:
  40. return {}
  41. if isinstance(extra_hosts_config, dict):
  42. return dict(extra_hosts_config)
  43. if isinstance(extra_hosts_config, list):
  44. extra_hosts_dict = {}
  45. for extra_hosts_line in extra_hosts_config:
  46. # TODO: validate string contains ':' ?
  47. host, ip = extra_hosts_line.split(':')
  48. extra_hosts_dict[host.strip()] = ip.strip()
  49. return extra_hosts_dict
  50. def normalize_paths_for_engine(external_path, internal_path):
  51. """Windows paths, c:\my\path\shiny, need to be changed to be compatible with
  52. the Engine. Volume paths are expected to be linux style /c/my/path/shiny/
  53. """
  54. if not IS_WINDOWS_PLATFORM:
  55. return external_path, internal_path
  56. if external_path:
  57. drive, tail = os.path.splitdrive(external_path)
  58. if drive:
  59. external_path = '/' + drive.lower().rstrip(':') + tail
  60. external_path = external_path.replace('\\', '/')
  61. return external_path, internal_path.replace('\\', '/')
  62. class VolumeSpec(namedtuple('_VolumeSpec', 'external internal mode')):
  63. @classmethod
  64. def parse(cls, volume_config):
  65. """Parse a volume_config path and split it into external:internal[:mode]
  66. parts to be returned as a valid VolumeSpec.
  67. """
  68. if IS_WINDOWS_PLATFORM:
  69. # relative paths in windows expand to include the drive, eg C:\
  70. # so we join the first 2 parts back together to count as one
  71. drive, tail = os.path.splitdrive(volume_config)
  72. parts = tail.split(":")
  73. if drive:
  74. parts[0] = drive + parts[0]
  75. else:
  76. parts = volume_config.split(':')
  77. if len(parts) > 3:
  78. raise ConfigurationError(
  79. "Volume %s has incorrect format, should be "
  80. "external:internal[:mode]" % volume_config)
  81. if len(parts) == 1:
  82. external, internal = normalize_paths_for_engine(
  83. None,
  84. os.path.normpath(parts[0]))
  85. else:
  86. external, internal = normalize_paths_for_engine(
  87. os.path.normpath(parts[0]),
  88. os.path.normpath(parts[1]))
  89. mode = 'rw'
  90. if len(parts) == 3:
  91. mode = parts[2]
  92. return cls(external, internal, mode)