From 3cb6482f8a2d06ad3679b850a44d1af5e05af5c2 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Tue, 14 Jan 2014 12:25:53 +0000 Subject: [PATCH] attach() method now parses multiplexing frames The previous version of `attach` didn't look like it was doing anything useful, as it just yielded 4096-byte chunks of raw /attach output, without doing any frame parsing. Now `attach` is identical to `logs`, except that by default it doesn't show historical output. `logs` is now a wrapper around `attach` which sets `logs=True`, and its behaviour is unchanged. --- README.md | 8 ++++++++ docker/client.py | 52 ++++++++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 28f97421..15f9b308 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,14 @@ Identical to the `docker logs` command. The `stream` parameter makes the `logs` function return a blocking generator you can iterate over to retrieve log output as it happens. +```python +c.attach(container, stdout=True, stderr=True, stream=False, logs=False) +``` + +The `logs` function is a wrapper around this one, which you can use +instead if you want to fetch/stream container output without first +retrieving the entire backlog. + ```python c.port(container, private_port) ``` diff --git a/docker/client.py b/docker/client.py index eb51ef84..cce252a7 100644 --- a/docker/client.py +++ b/docker/client.py @@ -281,15 +281,26 @@ class Client(requests.Session): break yield data - def attach(self, container): - socket = self.attach_socket(container) + def attach(self, container, stdout=True, stderr=True, + stream=False, logs=False): + if isinstance(container, dict): + container = container.get('Id') + params = { + 'logs': logs and 1 or 0, + 'stdout': stdout and 1 or 0, + 'stderr': stderr and 1 or 0, + 'stream': stream and 1 or 0, + } + u = self._url("/containers/{0}/attach".format(container)) + response = self._post(u, params=params, stream=stream) - while True: - chunk = socket.recv(4096) - if chunk: - yield chunk - else: - break + # Stream multi-plexing was introduced in API v1.6. + if utils.compare_version('1.6', self._version) < 0: + return stream and self._stream_result(response) or \ + self._result(response, binary=True) + + return stream and self._multiplexed_socket_stream_helper(response) or \ + ''.join([x for x in self._multiplexed_buffer_helper(response)]) def attach_socket(self, container, params=None, ws=False): if params is None: @@ -542,24 +553,13 @@ class Client(requests.Session): return self._result(response, json=True) def logs(self, container, stdout=True, stderr=True, stream=False): - if isinstance(container, dict): - container = container.get('Id') - params = { - 'logs': 1, - 'stdout': stdout and 1 or 0, - 'stderr': stderr and 1 or 0, - 'stream': stream and 1 or 0, - } - u = self._url("/containers/{0}/attach".format(container)) - response = self._post(u, params=params, stream=stream) - - # Stream multi-plexing was introduced in API v1.6. - if utils.compare_version('1.6', self._version) < 0: - return stream and self._stream_result(response) or \ - self._result(response, binary=True) - - return stream and self._multiplexed_socket_stream_helper(response) or \ - ''.join([x for x in self._multiplexed_buffer_helper(response)]) + return self.attach( + container, + stdout=stdout, + stderr=stderr, + stream=stream, + logs=True + ) def port(self, container, private_port): if isinstance(container, dict):