Pārlūkot izejas kodu

Returing 1 when a container exits with a non-zero exit code with --abort-on-container-exit is set. Signed-off-by: Kevin Broadwater <[email protected]>

Catching the first container to exit Signed-off-by: Kevin Broadwater <[email protected]>

Addressing feedback and fixing tests Signed-off-by: Kevin Broadwater <[email protected]>

Adding break and removing extra fixture files Signed-off-by: Kevin Broadwater <[email protected]>

Moving break Signed-off-by: Kevin Broadwater <[email protected]>
kbroadwater 8 gadi atpakaļ
vecāks
revīzija
6c3641fdb7

+ 12 - 9
compose/cli/log_printer.py

@@ -87,6 +87,13 @@ class LogPrinter(object):
         for line in consume_queue(queue, self.cascade_stop):
             remove_stopped_threads(thread_map)
 
+            if self.cascade_stop:
+                matching_container = [cont.name for cont in self.containers if cont.name == line]
+                if line in matching_container:
+                    # Returning the name of the container that started the
+                    # the cascade_stop so we can return the correct exit code
+                    return line
+
             if not line:
                 if not thread_map:
                     # There are no running containers left to tail, so exit
@@ -132,8 +139,8 @@ class QueueItem(namedtuple('_QueueItem', 'item is_stop exc')):
         return cls(None, None, exc)
 
     @classmethod
-    def stop(cls):
-        return cls(None, True, None)
+    def stop(cls, item=None):
+        return cls(item, True, None)
 
 
 def tail_container_logs(container, presenter, queue, log_args):
@@ -145,10 +152,9 @@ def tail_container_logs(container, presenter, queue, log_args):
     except Exception as e:
         queue.put(QueueItem.exception(e))
         return
-
     if log_args.get('follow'):
         queue.put(QueueItem.new(presenter.color_func(wait_on_exit(container))))
-    queue.put(QueueItem.stop())
+    queue.put(QueueItem.stop(container.name))
 
 
 def get_log_generator(container):
@@ -228,10 +234,7 @@ def consume_queue(queue, cascade_stop):
         if item.exc:
             raise item.exc
 
-        if item.is_stop:
-            if cascade_stop:
-                raise StopIteration
-            else:
-                continue
+        if item.is_stop and not cascade_stop:
+            continue
 
         yield item.item

+ 8 - 1
compose/cli/main.py

@@ -890,11 +890,18 @@ class TopLevelCommand(object):
                 cascade_stop,
                 event_stream=self.project.events(service_names=service_names))
             print("Attaching to", list_containers(log_printer.containers))
-            log_printer.run()
+            cascade_starter = log_printer.run()
 
             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
                 self.project.stop(service_names=service_names, timeout=timeout)
+                sys.exit(exit_code)
 
     @classmethod
     def version(cls, options):

+ 11 - 3
tests/acceptance/cli_test.py

@@ -1085,10 +1085,18 @@ class CLITestCase(DockerClientTestCase):
         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'])
+        self.base_dir = 'tests/fixtures/abort-on-container-exit-0'
+        proc = start_process(self.base_dir, ['up', '--abort-on-container-exit'])
+        wait_on_condition(ContainerCountCondition(self.project, 0))
+        proc.wait()
+        self.assertEqual(proc.returncode, 0)
+
+    def test_up_handles_abort_on_container_exit_code(self):
+        self.base_dir = 'tests/fixtures/abort-on-container-exit-1'
+        proc = start_process(self.base_dir, ['up', '--abort-on-container-exit'])
         wait_on_condition(ContainerCountCondition(self.project, 0))
+        proc.wait()
+        self.assertEqual(proc.returncode, 1)
 
     def test_exec_without_tty(self):
         self.base_dir = 'tests/fixtures/links-composefile'

+ 6 - 0
tests/fixtures/abort-on-container-exit-0/docker-compose.yml

@@ -0,0 +1,6 @@
+simple:
+  image: busybox:latest
+  command: top
+another:
+  image: busybox:latest
+  command: ls .

+ 6 - 0
tests/fixtures/abort-on-container-exit-1/docker-compose.yml

@@ -0,0 +1,6 @@
+simple:
+  image: busybox:latest
+  command: top
+another:
+  image: busybox:latest
+  command: ls /thecakeisalie

+ 4 - 2
tests/unit/cli/log_printer_test.py

@@ -187,11 +187,13 @@ class TestConsumeQueue(object):
         assert next(generator) == 'b'
 
     def test_item_is_stop_with_cascade_stop(self):
+        """Return the name of the container that caused the cascade_stop"""
         queue = Queue()
-        for item in QueueItem.stop(), QueueItem.new('a'), QueueItem.new('b'):
+        for item in QueueItem.stop('foobar-1'), QueueItem.new('a'), QueueItem.new('b'):
             queue.put(item)
 
-        assert list(consume_queue(queue, True)) == []
+        generator = consume_queue(queue, True)
+        assert next(generator) is 'foobar-1'
 
     def test_item_is_none_when_timeout_is_hit(self):
         queue = Queue()