Browse Source

Merge pull request #3980 from aanand/shell-out-attach

Attach interactively on Windows by shelling out
Joffrey F 9 years ago
parent
commit
de15ec835a
3 changed files with 73 additions and 42 deletions
  1. 18 20
      compose/cli/errors.py
  2. 50 22
      compose/cli/main.py
  3. 5 0
      compose/cli/utils.py

+ 18 - 20
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:
+def docker_not_found_msg(problem):
+    return "{} You might need to install Docker:\n\n{}".format(
+        problem, docker_install_url())
 
-    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/
-"""
 
+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 = """

+ 50 - 22
compose/cli/main.py

@@ -6,7 +6,9 @@ import contextlib
 import functools
 import json
 import logging
+import pipes
 import re
+import subprocess
 import sys
 from inspect import getdoc
 from operator import attrgetter
@@ -406,11 +408,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 +415,28 @@ class TopLevelCommand(object):
         command = [options['COMMAND']] + options['ARGS']
         tty = not options["-T"]
 
+        if IS_WINDOWS_PLATFORM and not detach:
+            args = ["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(call_docker(args))
+
         create_exec_options = {
             "privileged": options["--privileged"],
             "user": options["--user"],
@@ -675,12 +694,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 +982,20 @@ 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:
+                exit_code = call_docker(["start", "--attach", "--interactive", container.id])
+            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
@@ -1044,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)

+ 5 - 0
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__,