config.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. return from_dictionary(load_yaml(filename))
  49. def load_yaml(filename):
  50. try:
  51. with open(filename, 'r') as fh:
  52. return yaml.safe_load(fh)
  53. except IOError as e:
  54. raise ConfigurationError(six.text_type(e))
  55. def from_dictionary(dictionary):
  56. service_dicts = []
  57. for service_name, service_dict in list(dictionary.items()):
  58. if not isinstance(service_dict, dict):
  59. 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)
  60. service_dict = make_service_dict(service_name, service_dict)
  61. service_dicts.append(service_dict)
  62. return service_dicts
  63. def make_service_dict(name, options):
  64. service_dict = options.copy()
  65. service_dict['name'] = name
  66. return process_container_options(service_dict)
  67. def process_container_options(service_dict):
  68. for k in service_dict:
  69. if k not in ALLOWED_KEYS:
  70. msg = "Unsupported config option for %s service: '%s'" % (service_dict['name'], k)
  71. if k in DOCKER_CONFIG_HINTS:
  72. msg += " (did you mean '%s'?)" % DOCKER_CONFIG_HINTS[k]
  73. raise ConfigurationError(msg)
  74. for filename in get_env_files(service_dict):
  75. if not os.path.exists(filename):
  76. raise ConfigurationError("Couldn't find env file for service %s: %s" % (service_dict['name'], filename))
  77. if 'environment' in service_dict or 'env_file' in service_dict:
  78. service_dict['environment'] = build_environment(service_dict)
  79. return service_dict
  80. def parse_links(links):
  81. return dict(parse_link(l) for l in links)
  82. def parse_link(link):
  83. if ':' in link:
  84. source, alias = link.split(':', 1)
  85. return (alias, source)
  86. else:
  87. return (link, link)
  88. def get_env_files(options):
  89. env_files = options.get('env_file', [])
  90. if not isinstance(env_files, list):
  91. env_files = [env_files]
  92. return env_files
  93. def build_environment(options):
  94. env = {}
  95. for f in get_env_files(options):
  96. env.update(env_vars_from_file(f))
  97. env.update(parse_environment(options.get('environment')))
  98. return dict(resolve_env(k, v) for k, v in six.iteritems(env))
  99. def parse_environment(environment):
  100. if not environment:
  101. return {}
  102. if isinstance(environment, list):
  103. return dict(split_env(e) for e in environment)
  104. if isinstance(environment, dict):
  105. return environment
  106. raise ConfigurationError(
  107. "environment \"%s\" must be a list or mapping," %
  108. environment
  109. )
  110. def split_env(env):
  111. if '=' in env:
  112. return env.split('=', 1)
  113. else:
  114. return env, None
  115. def resolve_env(key, val):
  116. if val is not None:
  117. return key, val
  118. elif key in os.environ:
  119. return key, os.environ[key]
  120. else:
  121. return key, ''
  122. def env_vars_from_file(filename):
  123. """
  124. Read in a line delimited file of environment variables.
  125. """
  126. env = {}
  127. for line in open(filename, 'r'):
  128. line = line.strip()
  129. if line and not line.startswith('#'):
  130. k, v = split_env(line)
  131. env[k] = v
  132. return env
  133. class ConfigurationError(Exception):
  134. def __init__(self, msg):
  135. self.msg = msg
  136. def __str__(self):
  137. return self.msg