Explorar o código

Add flag for stops all containers if any container was stopped.

Signed-off-by: Evgeniy Dobrohvalov <[email protected]>
Evgeniy Dobrohvalov %!s(int64=10) %!d(string=hai) anos
pai
achega
bf48a781db

+ 3 - 2
compose/cli/log_printer.py

@@ -13,10 +13,11 @@ from compose.utils import split_buffer
 class LogPrinter(object):
     """Print logs from many containers to a single output stream."""
 
-    def __init__(self, containers, output=sys.stdout, monochrome=False):
+    def __init__(self, containers, output=sys.stdout, monochrome=False, cascade_stop=False):
         self.containers = containers
         self.output = utils.get_output_stream(output)
         self.monochrome = monochrome
+        self.cascade_stop = cascade_stop
 
     def run(self):
         if not self.containers:
@@ -24,7 +25,7 @@ class LogPrinter(object):
 
         prefix_width = max_name_width(self.containers)
         generators = list(self._make_log_generators(self.monochrome, prefix_width))
-        for line in Multiplexer(generators).loop():
+        for line in Multiplexer(generators, cascade_stop=self.cascade_stop).loop():
             self.output.write(line)
             self.output.flush()
 

+ 23 - 15
compose/cli/main.py

@@ -590,25 +590,33 @@ class TopLevelCommand(DocoptCommand):
         Usage: up [options] [SERVICE...]
 
         Options:
-            -d                     Detached mode: Run containers in the background,
-                                   print new container names.
-            --no-color             Produce monochrome output.
-            --no-deps              Don't start linked services.
-            --force-recreate       Recreate containers even if their configuration and
-                                   image haven't changed. Incompatible with --no-recreate.
-            --no-recreate          If containers already exist, don't recreate them.
-                                   Incompatible with --force-recreate.
-            --no-build             Don't build an image, even if it's missing
-            -t, --timeout TIMEOUT  Use this timeout in seconds for container shutdown
-                                   when attached or when containers are already
-                                   running. (default: 10)
+            -d                         Detached mode: Run containers in the background,
+                                       print new container names.
+                                       Incompatible with --abort-on-container-exit.
+            --no-color                 Produce monochrome output.
+            --no-deps                  Don't start linked services.
+            --force-recreate           Recreate containers even if their configuration
+                                       and image haven't changed.
+                                       Incompatible with --no-recreate.
+            --no-recreate              If containers already exist, don't recreate them.
+                                       Incompatible with --force-recreate.
+            --no-build                 Don't build an image, even if it's missing
+            --abort-on-container-exit  Stops all containers if any container was stopped.
+                                       Incompatible with -d.
+            -t, --timeout TIMEOUT      Use this timeout in seconds for container shutdown
+                                       when attached or when containers are already
+                                       running. (default: 10)
         """
         monochrome = options['--no-color']
         start_deps = not options['--no-deps']
+        cascade_stop = options['--abort-on-container-exit']
         service_names = options['SERVICE']
         timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
         detached = options.get('-d')
 
+        if detached and cascade_stop:
+            raise UserError("--abort-on-container-exit and -d cannot be combined.")
+
         to_attach = project.up(
             service_names=service_names,
             start_deps=start_deps,
@@ -619,7 +627,7 @@ class TopLevelCommand(DocoptCommand):
         )
 
         if not detached:
-            log_printer = build_log_printer(to_attach, service_names, monochrome)
+            log_printer = build_log_printer(to_attach, service_names, monochrome, cascade_stop)
             attach_to_logs(project, log_printer, service_names, timeout)
 
     def version(self, project, options):
@@ -695,13 +703,13 @@ def run_one_off_container(container_options, project, service, options):
     sys.exit(exit_code)
 
 
-def build_log_printer(containers, service_names, monochrome):
+def build_log_printer(containers, service_names, monochrome, cascade_stop):
     if service_names:
         containers = [
             container
             for container in containers if container.service in service_names
         ]
-    return LogPrinter(containers, monochrome=monochrome)
+    return LogPrinter(containers, monochrome=monochrome, cascade_stop=cascade_stop)
 
 
 def attach_to_logs(project, log_printer, service_names, timeout):

+ 6 - 2
compose/cli/multiplexer.py

@@ -20,8 +20,9 @@ class Multiplexer(object):
     parallel and yielding results as they come in.
     """
 
-    def __init__(self, iterators):
+    def __init__(self, iterators, cascade_stop=False):
         self.iterators = iterators
+        self.cascade_stop = cascade_stop
         self._num_running = len(iterators)
         self.queue = Queue()
 
@@ -36,7 +37,10 @@ class Multiplexer(object):
                     raise exception
 
                 if item is STOP:
-                    self._num_running -= 1
+                    if self.cascade_stop is True:
+                        break
+                    else:
+                        self._num_running -= 1
                 else:
                     yield item
             except Empty:

+ 16 - 12
docs/reference/up.md

@@ -15,18 +15,22 @@ parent = "smn_compose_cli"
 Usage: up [options] [SERVICE...]
 
 Options:
--d                     Detached mode: Run containers in the background,
-                       print new container names.
---no-color             Produce monochrome output.
---no-deps              Don't start linked services.
---force-recreate       Recreate containers even if their configuration and
-                       image haven't changed. Incompatible with --no-recreate.
---no-recreate          If containers already exist, don't recreate them.
-                       Incompatible with --force-recreate.
---no-build             Don't build an image, even if it's missing
--t, --timeout TIMEOUT  Use this timeout in seconds for container shutdown
-                       when attached or when containers are already
-                       running. (default: 10)
+-d                         Detached mode: Run containers in the background,
+                           print new container names.
+                           Incompatible with --abort-on-container-exit.
+--no-color                 Produce monochrome output.
+--no-deps                  Don't start linked services.
+--force-recreate           Recreate containers even if their configuration
+                           and image haven't changed.
+                           Incompatible with --no-recreate.
+--no-recreate              If containers already exist, don't recreate them.
+                           Incompatible with --force-recreate.
+--no-build                 Don't build an image, even if it's missing
+--abort-on-container-exit  Stops all containers if any container was stopped.
+                           Incompatible with -d.
+-t, --timeout TIMEOUT      Use this timeout in seconds for container shutdown
+                           when attached or when containers are already
+                           running. (default: 10)
 ```
 
 Builds, (re)creates, starts, and attaches to containers for a service.

+ 2 - 2
tests/unit/cli/main_test.py

@@ -36,7 +36,7 @@ class CLIMainTestCase(unittest.TestCase):
             mock_container('another', 1),
         ]
         service_names = ['web', 'db']
-        log_printer = build_log_printer(containers, service_names, True)
+        log_printer = build_log_printer(containers, service_names, True, False)
         self.assertEqual(log_printer.containers, containers[:3])
 
     def test_build_log_printer_all_services(self):
@@ -46,7 +46,7 @@ class CLIMainTestCase(unittest.TestCase):
             mock_container('other', 1),
         ]
         service_names = []
-        log_printer = build_log_printer(containers, service_names, True)
+        log_printer = build_log_printer(containers, service_names, True, False)
         self.assertEqual(log_printer.containers, containers)
 
     def test_attach_to_logs(self):

+ 18 - 0
tests/unit/multiplexer_test.py

@@ -2,6 +2,7 @@ from __future__ import absolute_import
 from __future__ import unicode_literals
 
 import unittest
+from time import sleep
 
 from compose.cli.multiplexer import Multiplexer
 
@@ -46,3 +47,20 @@ class MultiplexerTest(unittest.TestCase):
 
         with self.assertRaises(Problem):
             list(mux.loop())
+
+    def test_cascade_stop(self):
+        mux = Multiplexer([
+            ((lambda x: sleep(0.01) or x)(x) for x in ['after 0.01 sec T1',
+                                                       'after 0.02 sec T1',
+                                                       'after 0.03 sec T1']),
+            ((lambda x: sleep(0.02) or x)(x) for x in ['after 0.02 sec T2',
+                                                       'after 0.04 sec T2',
+                                                       'after 0.06 sec T2']),
+        ], cascade_stop=True)
+
+        self.assertEqual(
+            ['after 0.01 sec T1',
+             'after 0.02 sec T1',
+             'after 0.02 sec T2',
+             'after 0.03 sec T1'],
+            sorted(list(mux.loop())))