From 8b383ad79571fd15e47c88dff71e2a4836bd3ffd Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 14 Oct 2016 17:27:57 +0100 Subject: [PATCH 1/3] Refactor "docker not found" message generation, add Windows message Signed-off-by: Aanand Prasad --- compose/cli/errors.py | 38 ++++++++++++++++++-------------------- compose/cli/utils.py | 5 +++++ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/compose/cli/errors.py b/compose/cli/errors.py index f9a20b9e..4fdec08a 100644 --- a/compose/cli/errors.py +++ b/compose/cli/errors.py @@ -17,6 +17,7 @@ from .utils import call_silently from .utils import is_docker_for_mac_installed from .utils import is_mac from .utils import is_ubuntu +from .utils import is_windows log = logging.getLogger(__name__) @@ -90,11 +91,7 @@ def exit_with_error(msg): 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 + return docker_not_found_msg("Couldn't connect to Docker daemon.") if is_docker_for_mac_installed(): return conn_error_docker_for_mac if call_silently(['which', 'docker-machine']) == 0: @@ -102,25 +99,26 @@ def get_conn_error_message(url): 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/ -""" +def docker_not_found_msg(problem): + return "{} You might need to install Docker:\n\n{}".format( + problem, docker_install_url()) -docker_not_found_ubuntu = """ - Couldn't connect to Docker daemon. You might need to install Docker: - - https://docs.docker.com/engine/installation/ubuntulinux/ -""" +def docker_install_url(): + if is_mac(): + return docker_install_url_mac + elif is_ubuntu(): + return docker_install_url_ubuntu + elif is_windows(): + return docker_install_url_windows + else: + return docker_install_url_generic -docker_not_found_generic = """ - Couldn't connect to Docker daemon. You might need to install Docker: - - https://docs.docker.com/engine/installation/ -""" +docker_install_url_mac = "https://docs.docker.com/engine/installation/mac/" +docker_install_url_ubuntu = "https://docs.docker.com/engine/installation/ubuntulinux/" +docker_install_url_windows = "https://docs.docker.com/engine/installation/windows/" +docker_install_url_generic = "https://docs.docker.com/engine/installation/" conn_error_docker_machine = """ diff --git a/compose/cli/utils.py b/compose/cli/utils.py index e10a3674..580bd1b0 100644 --- a/compose/cli/utils.py +++ b/compose/cli/utils.py @@ -11,6 +11,7 @@ import sys import docker import compose +from ..const import IS_WINDOWS_PLATFORM # WindowsError is not defined on non-win32 platforms. Avoid runtime errors by # defining it as OSError (its parent class) if missing. @@ -73,6 +74,10 @@ def is_ubuntu(): return platform.system() == 'Linux' and platform.linux_distribution()[0] == 'Ubuntu' +def is_windows(): + return IS_WINDOWS_PLATFORM + + def get_version_info(scope): versioninfo = 'docker-compose version {}, build {}'.format( compose.__version__, From 8314a48a2e96a0e34e913fb6b3a2973f1bceec5a Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Mon, 26 Sep 2016 13:18:16 +0100 Subject: [PATCH 2/3] Attach interactively on Windows by shelling out Signed-off-by: Aanand Prasad --- compose/cli/main.py | 60 ++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index 438753a2..cbbb1325 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -7,6 +7,7 @@ import functools import json import logging import re +import subprocess import sys from inspect import getdoc from operator import attrgetter @@ -406,11 +407,6 @@ class TopLevelCommand(object): service = self.project.get_service(options['SERVICE']) detach = options['-d'] - if IS_WINDOWS_PLATFORM and not detach: - raise UserError( - "Interactive mode is not yet supported on Windows.\n" - "Please pass the -d flag when using `docker-compose exec`." - ) try: container = service.get_container(number=index) except ValueError as e: @@ -418,6 +414,28 @@ class TopLevelCommand(object): command = [options['COMMAND']] + options['ARGS'] tty = not options["-T"] + if IS_WINDOWS_PLATFORM and not detach: + args = ["docker", "exec"] + + if options["-d"]: + args += ["--detach"] + else: + args += ["--interactive"] + + if not options["-T"]: + args += ["--tty"] + + if options["--privileged"]: + args += ["--privileged"] + + if options["--user"]: + args += ["--user", options["--user"]] + + args += [container.id] + args += command + + sys.exit(subprocess.call(args)) + create_exec_options = { "privileged": options["--privileged"], "user": options["--user"], @@ -675,12 +693,6 @@ class TopLevelCommand(object): service = self.project.get_service(options['SERVICE']) detach = options['-d'] - if IS_WINDOWS_PLATFORM and not detach: - raise UserError( - "Interactive mode is not yet supported on Windows.\n" - "Please pass the -d flag when using `docker-compose run`." - ) - if options['--publish'] and options['--service-ports']: raise UserError( 'Service port mapping and manual port mapping ' @@ -969,17 +981,21 @@ def run_one_off_container(container_options, project, service, options): signals.set_signal_handler_to_shutdown() try: try: - operation = RunOperation( - project.client, - container.id, - interactive=not options['-T'], - logs=False, - ) - pty = PseudoTerminal(project.client, operation) - sockets = pty.sockets() - service.start_container(container) - pty.start(sockets) - exit_code = container.wait() + if IS_WINDOWS_PLATFORM: + args = ["docker", "start", "--attach", "--interactive", container.id] + exit_code = subprocess.call(args) + else: + operation = RunOperation( + project.client, + container.id, + interactive=not options['-T'], + logs=False, + ) + pty = PseudoTerminal(project.client, operation) + sockets = pty.sockets() + service.start_container(container) + pty.start(sockets) + exit_code = container.wait() except signals.ShutdownException: project.client.stop(container.id) exit_code = 1 From 925915eb2535a069d3eefe4c14d35e5964182ff9 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 14 Oct 2016 17:30:10 +0100 Subject: [PATCH 3/3] Show clear error when docker binary can't be found Signed-off-by: Aanand Prasad --- compose/cli/main.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compose/cli/main.py b/compose/cli/main.py index cbbb1325..58b95c2f 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -6,6 +6,7 @@ import contextlib import functools import json import logging +import pipes import re import subprocess import sys @@ -415,7 +416,7 @@ class TopLevelCommand(object): tty = not options["-T"] if IS_WINDOWS_PLATFORM and not detach: - args = ["docker", "exec"] + args = ["exec"] if options["-d"]: args += ["--detach"] @@ -434,7 +435,7 @@ class TopLevelCommand(object): args += [container.id] args += command - sys.exit(subprocess.call(args)) + sys.exit(call_docker(args)) create_exec_options = { "privileged": options["--privileged"], @@ -982,8 +983,7 @@ def run_one_off_container(container_options, project, service, options): try: try: if IS_WINDOWS_PLATFORM: - args = ["docker", "start", "--attach", "--interactive", container.id] - exit_code = subprocess.call(args) + exit_code = call_docker(["start", "--attach", "--interactive", container.id]) else: operation = RunOperation( project.client, @@ -1060,3 +1060,15 @@ def exit_if(condition, message, exit_code): if condition: log.error(message) raise SystemExit(exit_code) + + +def call_docker(args): + try: + executable_path = subprocess.check_output(["which", "docker"]).strip() + except subprocess.CalledProcessError: + raise UserError(errors.docker_not_found_msg("Couldn't find `docker` binary.")) + + args = [executable_path] + args + log.debug(" ".join(map(pipes.quote, args))) + + return subprocess.call(args)