From c0ca1afa458b374762bd752fa4712abcb37f7895 Mon Sep 17 00:00:00 2001 From: adw1n Date: Mon, 3 Sep 2018 05:54:12 +0200 Subject: [PATCH] Fix pulling images with `stream=True` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pulling an image with option `stream=True` like this: ``` client.api.pull('docker.io/user/repo_name', tag='latest', stream=True) ``` without consuming the generator oftentimes results in premature drop of the connection. Docker daemon tries to send progress of pulling the image to the client, but it encounters an error (broken pipe) and therefore cancells the pull action: ``` Thread 1 "dockerd-dev" received signal SIGPIPE, Broken pipe. ERRO[2018-09-03T05:12:35.746497638+02:00] Not continuing with pull after error: context canceled ``` As described in issue #2116, even though client receives response with status code 200, image is not pulled. Closes #2116 Signed-off-by: Przemysław Adamek --- docker/api/image.py | 3 ++- docker/models/images.py | 1 + tests/unit/models_containers_test.py | 3 ++- tests/unit/models_images_test.py | 6 ++++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docker/api/image.py b/docker/api/image.py index 5f05d8877e..2489b87c44 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -334,7 +334,8 @@ def pull(self, repository, tag=None, stream=False, auth_config=None, Args: repository (str): The repository to pull tag (str): The tag to pull - stream (bool): Stream the output as a generator + stream (bool): Stream the output as a generator. Make sure to + consume the generator, otherwise pull might get cancelled. auth_config (dict): Override the credentials that :py:meth:`~docker.api.daemon.DaemonApiMixin.login` has set for this request. ``auth_config`` should contain the ``username`` diff --git a/docker/models/images.py b/docker/models/images.py index 41632c6a36..c0b65e5d2a 100644 --- a/docker/models/images.py +++ b/docker/models/images.py @@ -409,6 +409,7 @@ def pull(self, repository, tag=None, **kwargs): if not tag: repository, tag = parse_repository_tag(repository) + kwargs['stream'] = False self.client.api.pull(repository, tag=tag, **kwargs) if tag: return self.get('{0}{2}{1}'.format( diff --git a/tests/unit/models_containers_test.py b/tests/unit/models_containers_test.py index 22dd241064..957035af0a 100644 --- a/tests/unit/models_containers_test.py +++ b/tests/unit/models_containers_test.py @@ -232,7 +232,8 @@ def test_run_pull(self): container = client.containers.run('alpine', 'sleep 300', detach=True) assert container.id == FAKE_CONTAINER_ID - client.api.pull.assert_called_with('alpine', platform=None, tag=None) + client.api.pull.assert_called_with('alpine', platform=None, tag=None, + stream=False) def test_run_with_error(self): client = make_fake_client() diff --git a/tests/unit/models_images_test.py b/tests/unit/models_images_test.py index 67832795fe..ef81a1599d 100644 --- a/tests/unit/models_images_test.py +++ b/tests/unit/models_images_test.py @@ -43,7 +43,8 @@ def test_load(self): def test_pull(self): client = make_fake_client() image = client.images.pull('test_image:latest') - client.api.pull.assert_called_with('test_image', tag='latest') + client.api.pull.assert_called_with('test_image', tag='latest', + stream=False) client.api.inspect_image.assert_called_with('test_image:latest') assert isinstance(image, Image) assert image.id == FAKE_IMAGE_ID @@ -51,7 +52,8 @@ def test_pull(self): def test_pull_multiple(self): client = make_fake_client() images = client.images.pull('test_image') - client.api.pull.assert_called_with('test_image', tag=None) + client.api.pull.assert_called_with('test_image', tag=None, + stream=False) client.api.images.assert_called_with( all=False, name='test_image', filters=None )