Merge pull request #2142 from dnephin/fix_error_cant_connect

Fix error when compose can't connect to docker
This commit is contained in:
Aanand Prasad 2015-10-07 11:22:32 +01:00
commit fda615008c
4 changed files with 71 additions and 45 deletions

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import unicode_literals from __future__ import unicode_literals
import contextlib
import logging import logging
import os import os
import re import re
@ -16,7 +17,6 @@ from .. import config
from ..project import Project from ..project import Project
from ..service import ConfigError from ..service import ConfigError
from .docker_client import docker_client from .docker_client import docker_client
from .docopt_command import DocoptCommand
from .utils import call_silently from .utils import call_silently
from .utils import is_mac from .utils import is_mac
from .utils import is_ubuntu from .utils import is_ubuntu
@ -24,40 +24,32 @@ from .utils import is_ubuntu
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Command(DocoptCommand): @contextlib.contextmanager
base_dir = '.' def friendly_error_message():
try:
def dispatch(self, *args, **kwargs): yield
try: except SSLError as e:
super(Command, self).dispatch(*args, **kwargs) raise errors.UserError('SSL error: %s' % e)
except SSLError as e: except ConnectionError:
raise errors.UserError('SSL error: %s' % e) if call_silently(['which', 'docker']) != 0:
except ConnectionError: if is_mac():
if call_silently(['which', 'docker']) != 0: raise errors.DockerNotFoundMac()
if is_mac(): elif is_ubuntu():
raise errors.DockerNotFoundMac() raise errors.DockerNotFoundUbuntu()
elif is_ubuntu():
raise errors.DockerNotFoundUbuntu()
else:
raise errors.DockerNotFoundGeneric()
elif call_silently(['which', 'boot2docker']) == 0:
raise errors.ConnectionErrorDockerMachine()
else: else:
raise errors.ConnectionErrorGeneric(self.get_client().base_url) raise errors.DockerNotFoundGeneric()
elif call_silently(['which', 'docker-machine']) == 0:
raise errors.ConnectionErrorDockerMachine()
else:
raise errors.ConnectionErrorGeneric(get_client().base_url)
def perform_command(self, options, handler, command_options):
if options['COMMAND'] in ('help', 'version'):
# Skip looking up the compose file.
handler(None, command_options)
return
project = get_project( def project_from_options(base_dir, options):
self.base_dir, return get_project(
get_config_path(options.get('--file')), base_dir,
project_name=options.get('--project-name'), get_config_path(options.get('--file')),
verbose=options.get('--verbose')) project_name=options.get('--project-name'),
verbose=options.get('--verbose'))
handler(project, command_options)
def get_config_path(file_option): def get_config_path(file_option):

View File

@ -25,9 +25,6 @@ class DocoptCommand(object):
def dispatch(self, argv, global_options): def dispatch(self, argv, global_options):
self.perform_command(*self.parse(argv, global_options)) self.perform_command(*self.parse(argv, global_options))
def perform_command(self, options, handler, command_options):
handler(command_options)
def parse(self, argv, global_options): def parse(self, argv, global_options):
options = docopt_full_help(getdoc(self), argv, **self.docopt_options()) options = docopt_full_help(getdoc(self), argv, **self.docopt_options())
command = options['COMMAND'] command = options['COMMAND']

View File

@ -23,7 +23,9 @@ from ..project import NoSuchService
from ..service import BuildError from ..service import BuildError
from ..service import ConvergenceStrategy from ..service import ConvergenceStrategy
from ..service import NeedsBuildError from ..service import NeedsBuildError
from .command import Command from .command import friendly_error_message
from .command import project_from_options
from .docopt_command import DocoptCommand
from .docopt_command import NoSuchCommand from .docopt_command import NoSuchCommand
from .errors import UserError from .errors import UserError
from .formatter import Formatter from .formatter import Formatter
@ -89,6 +91,15 @@ def setup_logging():
logging.getLogger("requests").propagate = False logging.getLogger("requests").propagate = False
def setup_console_handler(verbose):
if verbose:
console_handler.setFormatter(logging.Formatter('%(name)s.%(funcName)s: %(message)s'))
console_handler.setLevel(logging.DEBUG)
else:
console_handler.setFormatter(logging.Formatter())
console_handler.setLevel(logging.INFO)
# stolen from docopt master # stolen from docopt master
def parse_doc_section(name, source): def parse_doc_section(name, source):
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
@ -96,7 +107,7 @@ def parse_doc_section(name, source):
return [s.strip() for s in pattern.findall(source)] return [s.strip() for s in pattern.findall(source)]
class TopLevelCommand(Command): class TopLevelCommand(DocoptCommand):
"""Define and run multi-container applications with Docker. """Define and run multi-container applications with Docker.
Usage: Usage:
@ -130,20 +141,24 @@ class TopLevelCommand(Command):
version Show the Docker-Compose version information version Show the Docker-Compose version information
""" """
base_dir = '.'
def docopt_options(self): def docopt_options(self):
options = super(TopLevelCommand, self).docopt_options() options = super(TopLevelCommand, self).docopt_options()
options['version'] = get_version_info('compose') options['version'] = get_version_info('compose')
return options return options
def perform_command(self, options, *args, **kwargs): def perform_command(self, options, handler, command_options):
if options.get('--verbose'): setup_console_handler(options.get('--verbose'))
console_handler.setFormatter(logging.Formatter('%(name)s.%(funcName)s: %(message)s'))
console_handler.setLevel(logging.DEBUG)
else:
console_handler.setFormatter(logging.Formatter())
console_handler.setLevel(logging.INFO)
return super(TopLevelCommand, self).perform_command(options, *args, **kwargs) if options['COMMAND'] in ('help', 'version'):
# Skip looking up the compose file.
handler(None, command_options)
return
project = project_from_options(self.base_dir, options)
with friendly_error_message():
handler(project, command_options)
def build(self, project, options): def build(self, project, options):
""" """

View File

@ -0,0 +1,22 @@
from __future__ import absolute_import
import pytest
from requests.exceptions import ConnectionError
from compose.cli import errors
from compose.cli.command import friendly_error_message
from tests import mock
from tests import unittest
class FriendlyErrorMessageTestCase(unittest.TestCase):
def test_dispatch_generic_connection_error(self):
with pytest.raises(errors.ConnectionErrorGeneric):
with mock.patch(
'compose.cli.command.call_silently',
autospec=True,
side_effect=[0, 1]
):
with friendly_error_message():
raise ConnectionError()