interpolation.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import os
  2. from string import Template
  3. import six
  4. from .errors import ConfigurationError
  5. import logging
  6. log = logging.getLogger(__name__)
  7. def interpolate_environment_variables(config):
  8. return dict(
  9. (service_name, process_service(service_name, service_dict))
  10. for (service_name, service_dict) in config.items()
  11. )
  12. def process_service(service_name, service_dict):
  13. if not isinstance(service_dict, dict):
  14. raise ConfigurationError(
  15. 'Service "%s" doesn\'t have any configuration options. '
  16. 'All top level keys in your docker-compose.yml must map '
  17. 'to a dictionary of configuration options.' % service_name
  18. )
  19. return dict(
  20. (key, interpolate_value(service_name, key, val))
  21. for (key, val) in service_dict.items()
  22. )
  23. def interpolate_value(service_name, config_key, value):
  24. try:
  25. return recursive_interpolate(value)
  26. except InvalidInterpolation as e:
  27. raise ConfigurationError(
  28. 'Invalid interpolation format for "{config_key}" option '
  29. 'in service "{service_name}": "{string}"'
  30. .format(
  31. config_key=config_key,
  32. service_name=service_name,
  33. string=e.string,
  34. )
  35. )
  36. def recursive_interpolate(obj):
  37. if isinstance(obj, six.string_types):
  38. return interpolate(obj, os.environ)
  39. elif isinstance(obj, dict):
  40. return dict(
  41. (key, recursive_interpolate(val))
  42. for (key, val) in obj.items()
  43. )
  44. elif isinstance(obj, list):
  45. return map(recursive_interpolate, obj)
  46. else:
  47. return obj
  48. def interpolate(string, mapping):
  49. try:
  50. return Template(string).substitute(BlankDefaultDict(mapping))
  51. except ValueError:
  52. raise InvalidInterpolation(string)
  53. class BlankDefaultDict(dict):
  54. def __init__(self, mapping):
  55. super(BlankDefaultDict, self).__init__(mapping)
  56. def __getitem__(self, key):
  57. try:
  58. return super(BlankDefaultDict, self).__getitem__(key)
  59. except KeyError:
  60. log.warn(
  61. "The {} variable is not set. Substituting a blank string."
  62. .format(key)
  63. )
  64. return ""
  65. class InvalidInterpolation(Exception):
  66. def __init__(self, string):
  67. self.string = string