command.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. from __future__ import unicode_literals
  2. from __future__ import absolute_import
  3. from requests.exceptions import ConnectionError, SSLError
  4. import logging
  5. import os
  6. import re
  7. import six
  8. from .. import config
  9. from ..project import Project
  10. from ..service import ConfigError
  11. from .docopt_command import DocoptCommand
  12. from .utils import call_silently, is_mac, is_ubuntu
  13. from .docker_client import docker_client
  14. from . import verbose_proxy
  15. from . import errors
  16. from .. import __version__
  17. log = logging.getLogger(__name__)
  18. class Command(DocoptCommand):
  19. base_dir = '.'
  20. def dispatch(self, *args, **kwargs):
  21. try:
  22. super(Command, self).dispatch(*args, **kwargs)
  23. except SSLError as e:
  24. raise errors.UserError('SSL error: %s' % e)
  25. except ConnectionError:
  26. if call_silently(['which', 'docker']) != 0:
  27. if is_mac():
  28. raise errors.DockerNotFoundMac()
  29. elif is_ubuntu():
  30. raise errors.DockerNotFoundUbuntu()
  31. else:
  32. raise errors.DockerNotFoundGeneric()
  33. elif call_silently(['which', 'boot2docker']) == 0:
  34. raise errors.ConnectionErrorBoot2Docker()
  35. else:
  36. raise errors.ConnectionErrorGeneric(self.get_client().base_url)
  37. def perform_command(self, options, handler, command_options):
  38. if options['COMMAND'] == 'help':
  39. # Skip looking up the compose file.
  40. handler(None, command_options)
  41. return
  42. if 'FIG_FILE' in os.environ:
  43. log.warn('The FIG_FILE environment variable is deprecated.')
  44. log.warn('Please use COMPOSE_FILE instead.')
  45. explicit_config_path = options.get('--file') or os.environ.get('COMPOSE_FILE') or os.environ.get('FIG_FILE')
  46. project = self.get_project(
  47. self.get_config_path(explicit_config_path),
  48. project_name=options.get('--project-name'),
  49. verbose=options.get('--verbose'))
  50. handler(project, command_options)
  51. def get_client(self, verbose=False):
  52. client = docker_client()
  53. if verbose:
  54. version_info = six.iteritems(client.version())
  55. log.info("Compose version %s", __version__)
  56. log.info("Docker base_url: %s", client.base_url)
  57. log.info("Docker version: %s",
  58. ", ".join("%s=%s" % item for item in version_info))
  59. return verbose_proxy.VerboseProxy('docker', client)
  60. return client
  61. def get_project(self, config_path, project_name=None, verbose=False):
  62. try:
  63. return Project.from_dicts(
  64. self.get_project_name(config_path, project_name),
  65. config.load(config_path),
  66. self.get_client(verbose=verbose))
  67. except ConfigError as e:
  68. raise errors.UserError(six.text_type(e))
  69. def get_project_name(self, config_path, project_name=None):
  70. def normalize_name(name):
  71. return re.sub(r'[^a-z0-9]', '', name.lower())
  72. if 'FIG_PROJECT_NAME' in os.environ:
  73. log.warn('The FIG_PROJECT_NAME environment variable is deprecated.')
  74. log.warn('Please use COMPOSE_PROJECT_NAME instead.')
  75. project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME') or os.environ.get('FIG_PROJECT_NAME')
  76. if project_name is not None:
  77. return normalize_name(project_name)
  78. project = os.path.basename(os.path.dirname(os.path.abspath(config_path)))
  79. if project:
  80. return normalize_name(project)
  81. return 'default'
  82. def get_config_path(self, file_path=None):
  83. if file_path:
  84. return os.path.join(self.base_dir, file_path)
  85. supported_filenames = [
  86. 'docker-compose.yml',
  87. 'docker-compose.yaml',
  88. 'fig.yml',
  89. 'fig.yaml',
  90. ]
  91. def expand(filename):
  92. return os.path.join(self.base_dir, filename)
  93. candidates = [filename for filename in supported_filenames if os.path.exists(expand(filename))]
  94. if len(candidates) == 0:
  95. raise errors.ComposeFileNotFound(supported_filenames)
  96. winner = candidates[0]
  97. if len(candidates) > 1:
  98. log.warning("Found multiple config files with supported names: %s", ", ".join(candidates))
  99. log.warning("Using %s\n", winner)
  100. if winner == 'docker-compose.yaml':
  101. log.warning("Please be aware that .yml is the expected extension "
  102. "in most cases, and using .yaml can cause compatibility "
  103. "issues in future.\n")
  104. if winner.startswith("fig."):
  105. log.warning("%s is deprecated and will not be supported in future. "
  106. "Please rename your config file to docker-compose.yml\n" % winner)
  107. return expand(winner)