Repository: docker/docker-py

Issue:
docker.Client.logs(tail=0) returns all logs instead of no logs
This is a confusing behavior.  It also makes it harder to follow a container's logs (with `stream=True`) without getting at least some old log data.  A work-around is required, specifying `tail=1` and then discarding the first line of output.



Discussion:
I believe this is the behaviour of the API (http://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/#get-container-logs), so we shouldn't be modifying that on the client.  You might need to open this issue on the docker project.

The CLI behaves properly, so this is most likely a docker-py bug.


Resolving patch:
diff --git a/docker/api/container.py b/docker/api/container.py
--- a/docker/api/container.py
+++ b/docker/api/container.py
@@ -192,7 +192,7 @@ def logs(self, container, stdout=True, stderr=True, stream=False,
                       'follow': stream and 1 or 0,
                       }
             if utils.compare_version('1.13', self._version) >= 0:
-                if tail != 'all' and (not isinstance(tail, int) or tail <= 0):
+                if tail != 'all' and (not isinstance(tail, int) or tail < 0):
                     tail = 'all'
                 params['tail'] = tail
             url = self._url("/containers/{0}/logs", container)


Test patch:
diff --git a/tests/integration/container_test.py b/tests/integration/container_test.py
--- a/tests/integration/container_test.py
+++ b/tests/integration/container_test.py
@@ -613,8 +613,8 @@ def test_logs(self):
             BUSYBOX, 'echo {0}'.format(snippet)
         )
         id = container['Id']
-        self.client.start(id)
         self.tmp_containers.append(id)
+        self.client.start(id)
         exitcode = self.client.wait(id)
         self.assertEqual(exitcode, 0)
         logs = self.client.logs(id)
@@ -627,29 +627,29 @@ def test_logs_tail_option(self):
             BUSYBOX, 'echo "{0}"'.format(snippet)
         )
         id = container['Id']
-        self.client.start(id)
         self.tmp_containers.append(id)
+        self.client.start(id)
         exitcode = self.client.wait(id)
         self.assertEqual(exitcode, 0)
         logs = self.client.logs(id, tail=1)
-        self.assertEqual(logs, ('Line2\n').encode(encoding='ascii'))
+        self.assertEqual(logs, 'Line2\n'.encode(encoding='ascii'))
 
-#     def test_logs_streaming(self):
-#         snippet = 'Flowering Nights (Sakuya Iyazoi)'
-#         container = self.client.create_container(
-#             BUSYBOX, 'echo {0}'.format(snippet)
-#         )
-#         id = container['Id']
-#         self.client.start(id)
-#         self.tmp_containers.append(id)
-#         logs = bytes() if six.PY3 else str()
-#         for chunk in self.client.logs(id, stream=True):
-#             logs += chunk
+    def test_logs_streaming(self):
+        snippet = 'Flowering Nights (Sakuya Iyazoi)'
+        container = self.client.create_container(
+            BUSYBOX, 'echo {0}'.format(snippet)
+        )
+        id = container['Id']
+        self.tmp_containers.append(id)
+        self.client.start(id)
+        logs = six.binary_type()
+        for chunk in self.client.logs(id, stream=True):
+            logs += chunk
 
-#         exitcode = self.client.wait(id)
-#         self.assertEqual(exitcode, 0)
+        exitcode = self.client.wait(id)
+        self.assertEqual(exitcode, 0)
 
-#         self.assertEqual(logs, (snippet + '\n').encode(encoding='ascii'))
+        self.assertEqual(logs, (snippet + '\n').encode(encoding='ascii'))
 
     def test_logs_with_dict_instead_of_id(self):
         snippet = 'Flowering Nights (Sakuya Iyazoi)'
@@ -657,13 +657,26 @@ def test_logs_with_dict_instead_of_id(self):
             BUSYBOX, 'echo {0}'.format(snippet)
         )
         id = container['Id']
-        self.client.start(id)
         self.tmp_containers.append(id)
+        self.client.start(id)
         exitcode = self.client.wait(id)
         self.assertEqual(exitcode, 0)
         logs = self.client.logs(container)
         self.assertEqual(logs, (snippet + '\n').encode(encoding='ascii'))
 
+    def test_logs_with_tail_0(self):
+        snippet = 'Flowering Nights (Sakuya Iyazoi)'
+        container = self.client.create_container(
+            BUSYBOX, 'echo "{0}"'.format(snippet)
+        )
+        id = container['Id']
+        self.tmp_containers.append(id)
+        self.client.start(id)
+        exitcode = self.client.wait(id)
+        self.assertEqual(exitcode, 0)
+        logs = self.client.logs(id, tail=0)
+        self.assertEqual(logs, ''.encode(encoding='ascii'))
+
 
 class DiffTest(api_test.BaseTestCase):
     def test_diff(self):