diff --git a/compose/cli/main.py b/compose/cli/main.py index f5d2429f3..a7aec945e 100644 --- a/compose/cli/main.py +++ b/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 diff --git a/contrib/completion/bash/docker-compose b/contrib/completion/bash/docker-compose index b90d1bb14..f4b9342f3 100644 --- a/contrib/completion/bash/docker-compose +++ b/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 diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 9379a6c31..cc7bc5dfe 100644 --- a/tests/acceptance/cli_test.py +++ b/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 diff --git a/tests/fixtures/exit-code-from/docker-compose.yml b/tests/fixtures/exit-code-from/docker-compose.yml new file mode 100644 index 000000000..687e78b97 --- /dev/null +++ b/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