1
0

config.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import os
  2. import yaml
  3. import six
  4. DOCKER_CONFIG_KEYS = [
  5. 'cap_add',
  6. 'cap_drop',
  7. 'cpu_shares',
  8. 'command',
  9. 'detach',
  10. 'dns',
  11. 'dns_search',
  12. 'domainname',
  13. 'entrypoint',
  14. 'env_file',
  15. 'environment',
  16. 'hostname',
  17. 'image',
  18. 'links',
  19. 'mem_limit',
  20. 'net',
  21. 'ports',
  22. 'privileged',
  23. 'restart',
  24. 'stdin_open',
  25. 'tty',
  26. 'user',
  27. 'volumes',
  28. 'volumes_from',
  29. 'working_dir',
  30. ]
  31. ALLOWED_KEYS = DOCKER_CONFIG_KEYS + [
  32. 'build',
  33. 'expose',
  34. 'external_links',
  35. 'name',
  36. ]
  37. DOCKER_CONFIG_HINTS = {
  38. 'cpu_share' : 'cpu_shares',
  39. 'link' : 'links',
  40. 'port' : 'ports',
  41. 'privilege' : 'privileged',
  42. 'priviliged': 'privileged',
  43. 'privilige' : 'privileged',
  44. 'volume' : 'volumes',
  45. 'workdir' : 'working_dir',
  46. }
  47. def load(filename):
  48. working_dir = os.path.dirname(filename)
  49. return from_dictionary(load_yaml(filename), working_dir=working_dir)
  50. def load_yaml(filename):
  51. try:
  52. with open(filename, 'r') as fh:
  53. return yaml.safe_load(fh)
  54. except IOError as e:
  55. raise ConfigurationError(six.text_type(e))
  56. def from_dictionary(dictionary, working_dir=None):
  57. service_dicts = []
  58. for service_name, service_dict in list(dictionary.items()):
  59. if not isinstance(service_dict, dict):
  60. raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
  61. service_dict = make_service_dict(service_name, service_dict, working_dir=working_dir)
  62. service_dicts.append(service_dict)
  63. return service_dicts
  64. def make_service_dict(name, options, working_dir=None):
  65. service_dict = options.copy()
  66. service_dict['name'] = name
  67. service_dict = resolve_environment(service_dict, working_dir=working_dir)
  68. return process_container_options(service_dict, working_dir=working_dir)
  69. def process_container_options(service_dict, working_dir=None):
  70. for k in service_dict:
  71. if k not in ALLOWED_KEYS:
  72. msg = "Unsupported config option for %s service: '%s'" % (service_dict['name'], k)
  73. if k in DOCKER_CONFIG_HINTS:
  74. msg += " (did you mean '%s'?)" % DOCKER_CONFIG_HINTS[k]
  75. raise ConfigurationError(msg)
  76. return service_dict
  77. def parse_links(links):
  78. return dict(parse_link(l) for l in links)
  79. def parse_link(link):
  80. if ':' in link:
  81. source, alias = link.split(':', 1)
  82. return (alias, source)
  83. else:
  84. return (link, link)
  85. def get_env_files(options, working_dir=None):
  86. if 'env_file' not in options:
  87. return {}
  88. if working_dir is None:
  89. raise Exception("No working_dir passed to get_env_files()")
  90. env_files = options.get('env_file', [])
  91. if not isinstance(env_files, list):
  92. env_files = [env_files]
  93. return [expand_path(working_dir, path) for path in env_files]
  94. def resolve_environment(service_dict, working_dir=None):
  95. service_dict = service_dict.copy()
  96. if 'environment' not in service_dict and 'env_file' not in service_dict:
  97. return service_dict
  98. env = {}
  99. if 'env_file' in service_dict:
  100. for f in get_env_files(service_dict, working_dir=working_dir):
  101. env.update(env_vars_from_file(f))
  102. del service_dict['env_file']
  103. env.update(parse_environment(service_dict.get('environment')))
  104. env = dict(resolve_env_var(k, v) for k, v in six.iteritems(env))
  105. service_dict['environment'] = env
  106. return service_dict
  107. def parse_environment(environment):
  108. if not environment:
  109. return {}
  110. if isinstance(environment, list):
  111. return dict(split_env(e) for e in environment)
  112. if isinstance(environment, dict):
  113. return environment
  114. raise ConfigurationError(
  115. "environment \"%s\" must be a list or mapping," %
  116. environment
  117. )
  118. def split_env(env):
  119. if '=' in env:
  120. return env.split('=', 1)
  121. else:
  122. return env, None
  123. def resolve_env_var(key, val):
  124. if val is not None:
  125. return key, val
  126. elif key in os.environ:
  127. return key, os.environ[key]
  128. else:
  129. return key, ''
  130. def env_vars_from_file(filename):
  131. """
  132. Read in a line delimited file of environment variables.
  133. """
  134. if not os.path.exists(filename):
  135. raise ConfigurationError("Couldn't find env file: %s" % filename)
  136. env = {}
  137. for line in open(filename, 'r'):
  138. line = line.strip()
  139. if line and not line.startswith('#'):
  140. k, v = split_env(line)
  141. env[k] = v
  142. return env
  143. def expand_path(working_dir, path):
  144. return os.path.abspath(os.path.join(working_dir, path))
  145. class ConfigurationError(Exception):
  146. def __init__(self, msg):
  147. self.msg = msg
  148. def __str__(self):
  149. return self.msg