浏览代码

Merge branch 'forward-exit' of https://github.com/TheClimateCorporation/compose into TheClimateCorporation-forward-exit

Signed-off-by: Joffrey F <[email protected]>
Joffrey F 8 年之前
父节点
当前提交
6986a994a8

+ 46 - 6
compose/cli/main.py

@@ -858,8 +858,11 @@ class TopLevelCommand(object):
                                        running. (default: 10)
             --remove-orphans           Remove containers for services not
                                        defined in the Compose file
+            --exit-code-from SERVICE   Return the exit code of the selected service container.
+                                       Requires --abort-on-container-exit.
         """
         start_deps = not options['--no-deps']
+        exit_value_from = exitval_from_opts(options, self.project)
         cascade_stop = options['--abort-on-container-exit']
         service_names = options['SERVICE']
         timeout = timeout_from_opts(options)
@@ -882,9 +885,11 @@ class TopLevelCommand(object):
             if detached:
                 return
 
+            attached_containers = filter_containers_to_service_names(to_attach, service_names)
+
             log_printer = log_printer_from_project(
                 self.project,
-                filter_containers_to_service_names(to_attach, service_names),
+                attached_containers,
                 options['--no-color'],
                 {'follow': True},
                 cascade_stop,
@@ -894,12 +899,34 @@ class TopLevelCommand(object):
 
             if cascade_stop:
                 print("Aborting on container exit...")
+
                 exit_code = 0
-                for e in self.project.containers(service_names=options['SERVICE'], stopped=True):
-                    if (not e.is_running and cascade_starter == e.name):
-                        if not e.exit_code == 0:
-                            exit_code = e.exit_code
-                            break
+                if exit_value_from:
+                    candidates = filter(
+                        lambda c: c.service == exit_value_from,
+                        attached_containers)
+                    if not candidates:
+                        log.error(
+                            'No containers matching the spec "{0}" '
+                            'were run.'.format(exit_value_from)
+                        )
+                        exit_code = 2
+                    elif len(candidates) > 1:
+                        exit_values = filter(
+                            lambda e: e != 0,
+                            [c.inspect()['State']['ExitCode'] for c in candidates]
+                        )
+
+                        exit_code = exit_values[0]
+                    else:
+                        exit_code = candidates[0].inspect()['State']['ExitCode']
+                else:
+                    for e in self.project.containers(service_names=options['SERVICE'], stopped=True):
+                        if (not e.is_running and cascade_starter == e.name):
+                            if not e.exit_code == 0:
+                                exit_code = e.exit_code
+                                break
+
                 self.project.stop(service_names=service_names, timeout=timeout)
                 sys.exit(exit_code)
 
@@ -939,6 +966,19 @@ def timeout_from_opts(options):
     return None if timeout is None else int(timeout)
 
 
+def exitval_from_opts(options, project):
+    exit_value_from = options.get('--exit-code-from')
+    if exit_value_from:
+        if not options.get('--abort-on-container-exit'):
+            log.warn('using --exit-code-from implies --abort-on-container-exit')
+            options['--abort-on-container-exit'] = True
+        if exit_value_from not in [s.name for s in project.get_services()]:
+            log.error('No service named "%s" was found in your compose file.',
+                      exit_value_from)
+            sys.exit(2)
+    return exit_value_from
+
+
 def image_type_from_opt(flag, value):
     if not value:
         return ImageType.none

+ 1 - 1
contrib/completion/bash/docker-compose

@@ -467,7 +467,7 @@ _docker_compose_up() {
 
 	case "$cur" in
 		-*)
-			COMPREPLY=( $( compgen -W "--abort-on-container-exit --build -d --force-recreate --help --no-build --no-color --no-deps --no-recreate --timeout -t --remove-orphans" -- "$cur" ) )
+			COMPREPLY=( $( compgen -W "--exit-code-from --abort-on-container-exit --build -d --force-recreate --help --no-build --no-color --no-deps --no-recreate --timeout -t --remove-orphans" -- "$cur" ) )
 			;;
 		*)
 			__docker_compose_services_all

+ 10 - 0
tests/acceptance/cli_test.py

@@ -1976,3 +1976,13 @@ class CLITestCase(DockerClientTestCase):
         self.dispatch(['up', '-d'])
         result = self.dispatch(['top'])
         assert result.stdout.count("top") == 4
+
+    def test_forward_exitval(self):
+        self.base_dir = 'tests/fixtures/exit-code-from'
+        proc = start_process(
+            self.base_dir,
+            ['up', '--abort-on-container-exit', '--exit-code-from', 'another'])
+
+        result = wait_on_process(proc, returncode=1)
+
+        assert 'exitcodefrom_another_1 exited with code 1' in result.stdout

+ 6 - 0
tests/fixtures/exit-code-from/docker-compose.yml

@@ -0,0 +1,6 @@
+simple:
+  image: busybox:latest
+  command: sh -c "echo hello && tail -f /dev/null"
+another:
+  image: busybox:latest
+  command: /bin/false