| from __future__ import absolute_import |
| from __future__ import unicode_literals |
| |
| import contextlib |
| import logging |
| import socket |
| from textwrap import dedent |
| |
| from docker.errors import APIError |
| from requests.exceptions import ConnectionError as RequestsConnectionError |
| from requests.exceptions import ReadTimeout |
| from requests.exceptions import SSLError |
| from requests.packages.urllib3.exceptions import ReadTimeoutError |
| |
| from ..const import API_VERSION_TO_ENGINE_VERSION |
| from .utils import call_silently |
| from .utils import is_docker_for_mac_installed |
| from .utils import is_mac |
| from .utils import is_ubuntu |
| |
| |
| log = logging.getLogger(__name__) |
| |
| |
| class UserError(Exception): |
| |
| def __init__(self, msg): |
| self.msg = dedent(msg).strip() |
| |
| def __unicode__(self): |
| return self.msg |
| |
| __str__ = __unicode__ |
| |
| |
| class ConnectionError(Exception): |
| pass |
| |
| |
| @contextlib.contextmanager |
| def handle_connection_errors(client): |
| try: |
| yield |
| except SSLError as e: |
| log.error('SSL error: %s' % e) |
| raise ConnectionError() |
| except RequestsConnectionError as e: |
| if e.args and isinstance(e.args[0], ReadTimeoutError): |
| log_timeout_error(client.timeout) |
| raise ConnectionError() |
| exit_with_error(get_conn_error_message(client.base_url)) |
| except APIError as e: |
| log_api_error(e, client.api_version) |
| raise ConnectionError() |
| except (ReadTimeout, socket.timeout) as e: |
| log_timeout_error() |
| raise ConnectionError() |
| |
| |
| def log_timeout_error(timeout): |
| log.error( |
| "An HTTP request took too long to complete. Retry with --verbose to " |
| "obtain debug information.\n" |
| "If you encounter this issue regularly because of slow network " |
| "conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher " |
| "value (current value: %s)." % timeout) |
| |
| |
| def log_api_error(e, client_version): |
| if b'client is newer than server' not in e.explanation: |
| log.error(e.explanation) |
| return |
| |
| version = API_VERSION_TO_ENGINE_VERSION.get(client_version) |
| if not version: |
| # They've set a custom API version |
| log.error(e.explanation) |
| return |
| |
| log.error( |
| "The Docker Engine version is less than the minimum required by " |
| "Compose. Your current project requires a Docker Engine of " |
| "version {version} or greater.".format(version=version)) |
| |
| |
| def exit_with_error(msg): |
| log.error(dedent(msg).strip()) |
| raise ConnectionError() |
| |
| |
| def get_conn_error_message(url): |
| if call_silently(['which', 'docker']) != 0: |
| if is_mac(): |
| return docker_not_found_mac |
| if is_ubuntu(): |
| return docker_not_found_ubuntu |
| return docker_not_found_generic |
| if is_docker_for_mac_installed(): |
| return conn_error_docker_for_mac |
| if call_silently(['which', 'docker-machine']) == 0: |
| return conn_error_docker_machine |
| return conn_error_generic.format(url=url) |
| |
| |
| docker_not_found_mac = """ |
| Couldn't connect to Docker daemon. You might need to install Docker: |
| |
| https://docs.docker.com/engine/installation/mac/ |
| """ |
| |
| |
| docker_not_found_ubuntu = """ |
| Couldn't connect to Docker daemon. You might need to install Docker: |
| |
| https://docs.docker.com/engine/installation/ubuntulinux/ |
| """ |
| |
| |
| docker_not_found_generic = """ |
| Couldn't connect to Docker daemon. You might need to install Docker: |
| |
| https://docs.docker.com/engine/installation/ |
| """ |
| |
| |
| conn_error_docker_machine = """ |
| Couldn't connect to Docker daemon - you might need to run `docker-machine start default`. |
| """ |
| |
| conn_error_docker_for_mac = """ |
| Couldn't connect to Docker daemon. You might need to start Docker for Mac. |
| """ |
| |
| |
| conn_error_generic = """ |
| Couldn't connect to Docker daemon at {url} - is it running? |
| |
| If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable. |
| """ |