diff --git a/compose/cli/log_printer.py b/compose/cli/log_printer.py index 85fef794f..b7abc007e 100644 --- a/compose/cli/log_printer.py +++ b/compose/cli/log_printer.py @@ -5,6 +5,7 @@ import sys from itertools import cycle from . import colors +from . import signals from .multiplexer import Multiplexer from compose import utils from compose.utils import split_buffer @@ -41,7 +42,7 @@ class LogPrinter(object): for color_func, container in zip(color_funcs, self.containers): generator_func = get_log_generator(container) prefix = color_func(build_log_prefix(container, prefix_width)) - yield generator_func(container, prefix, color_func) + yield generator_func(container, prefix, color_func, self.cascade_stop) def build_log_prefix(container, prefix_width): @@ -64,7 +65,7 @@ def get_log_generator(container): return build_no_log_generator -def build_no_log_generator(container, prefix, color_func): +def build_no_log_generator(container, prefix, color_func, cascade_stop): """Return a generator that prints a warning about logs and waits for container to exit. """ @@ -72,9 +73,11 @@ def build_no_log_generator(container, prefix, color_func): prefix, container.log_driver) yield color_func(wait_on_exit(container)) + if cascade_stop: + raise signals.CascadeStopException() -def build_log_generator(container, prefix, color_func): +def build_log_generator(container, prefix, color_func, cascade_stop): # if the container doesn't have a log_stream we need to attach to container # before log printer starts running if container.log_stream is None: @@ -86,6 +89,8 @@ def build_log_generator(container, prefix, color_func): for line in line_generator: yield prefix + line yield color_func(wait_on_exit(container)) + if cascade_stop: + raise signals.CascadeStopException() def wait_on_exit(container): diff --git a/compose/cli/main.py b/compose/cli/main.py index cc15fa051..5a7ac8d47 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -774,6 +774,9 @@ def up_shutdown_context(project, service_names, timeout, detached): except signals.ShutdownException: print("Gracefully stopping... (press Ctrl+C again to force)") project.stop(service_names=service_names, timeout=timeout) + except signals.CascadeStopException: + print("Aborting on container exit... (press Ctrl+C to force)") + project.stop(service_names=service_names, timeout=timeout) except signals.ShutdownException: project.kill(service_names=service_names) sys.exit(2) diff --git a/compose/cli/signals.py b/compose/cli/signals.py index 68a0598e1..808700df3 100644 --- a/compose/cli/signals.py +++ b/compose/cli/signals.py @@ -8,6 +8,10 @@ class ShutdownException(Exception): pass +class CascadeStopException(Exception): + pass + + def shutdown(signal, frame): raise ShutdownException() diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 318ab3d3f..23427e99a 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -746,6 +746,12 @@ class CLITestCase(DockerClientTestCase): os.kill(proc.pid, signal.SIGTERM) wait_on_condition(ContainerCountCondition(self.project, 0)) + def test_up_handles_abort_on_container_exit(self): + start_process(self.base_dir, ['up', '--abort-on-container-exit']) + wait_on_condition(ContainerCountCondition(self.project, 2)) + self.project.stop(['simple']) + wait_on_condition(ContainerCountCondition(self.project, 0)) + def test_run_service_without_links(self): self.base_dir = 'tests/fixtures/links-composefile' self.dispatch(['run', 'console', '/bin/true'])