From 5e143c05df1926c2db3b10bc630694b0a1cfa1f6 Mon Sep 17 00:00:00 2001 From: Francesco Zardi Date: Fri, 13 Sep 2024 22:06:39 +0200 Subject: [PATCH] Check presence of errors in server response to image push Fixes: #3277 Signed-off-by: Francesco Zardi --- docker/api/image.py | 11 ++++++++--- tests/unit/api_image_test.py | 19 +++++++++++++++++++ tests/unit/fake_api.py | 9 +++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/docker/api/image.py b/docker/api/image.py index 85109473b..8ef639e28 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -1,6 +1,7 @@ import logging import os +from ..utils.json_stream import json_stream from .. import auth, errors, utils from ..constants import DEFAULT_DATA_CHUNK_SIZE @@ -494,10 +495,14 @@ def push(self, repository, tag=None, stream=False, auth_config=None, self._raise_for_status(response) - if stream: - return self._stream_helper(response, decode=decode) + for chunk in json_stream(self._stream_helper(response, decode=decode)): + if 'error' in chunk: + raise errors.APIError(chunk['error'], response=response) + if stream: + yield chunk - return self._result(response) + if not stream: + return response.text @utils.check_resource('image') def remove_image(self, image, force=False, noprune=False): diff --git a/tests/unit/api_image_test.py b/tests/unit/api_image_test.py index 148109d37..9aa59662f 100644 --- a/tests/unit/api_image_test.py +++ b/tests/unit/api_image_test.py @@ -227,6 +227,24 @@ def test_push_image(self): timeout=DEFAULT_TIMEOUT_SECONDS ) + def test_push_image_bad_auth(self): + with pytest.raises(docker.errors.APIError, match='you shall not pass'): + with mock.patch('docker.auth.resolve_authconfig', + fake_resolve_authconfig): + self.client.push(fake_api.FAKE_IMAGE_NAME_BAD_AUTH) + + fake_request.assert_called_with( + 'POST', + f"{url_prefix}images/test_image_bad_auth/push", + params={ + 'tag': None + }, + data='{}', + headers={'Content-Type': 'application/json'}, + stream=False, + timeout=DEFAULT_TIMEOUT_SECONDS + ) + def test_push_image_with_tag(self): with mock.patch('docker.auth.resolve_authconfig', fake_resolve_authconfig): @@ -271,6 +289,7 @@ def test_push_image_with_auth(self): timeout=DEFAULT_TIMEOUT_SECONDS ) + def test_push_image_stream(self): with mock.patch('docker.auth.resolve_authconfig', fake_resolve_authconfig): diff --git a/tests/unit/fake_api.py b/tests/unit/fake_api.py index 03e53cc64..4a820d5a1 100644 --- a/tests/unit/fake_api.py +++ b/tests/unit/fake_api.py @@ -9,6 +9,7 @@ FAKE_EXEC_ID = 'b098ec855f10434b5c7c973c78484208223a83f663ddaefb0f02a242840cb1c7' FAKE_NETWORK_ID = '1999cfb42e414483841a125ade3c276c3cb80cb3269b14e339354ac63a31b02c' FAKE_IMAGE_NAME = 'test_image' +FAKE_IMAGE_NAME_BAD_AUTH = 'test_image_bad_auth' FAKE_TARBALL_PATH = '/path/to/tarball' FAKE_REPO_NAME = 'repo' FAKE_TAG_NAME = 'tag' @@ -359,6 +360,12 @@ def post_fake_push(): return status_code, response +def post_fake_push_bad_auth(): + status_code = 200 + response = [{'Id': FAKE_IMAGE_ID}, {'error':'you shall not pass'}] + return status_code, response + + def post_fake_build_container(): status_code = 200 response = {'Id': FAKE_CONTAINER_ID} @@ -603,6 +610,8 @@ def post_fake_config(): get_fake_insert_image, f'{prefix}/{CURRENT_VERSION}/images/test_image/push': post_fake_push, + f'{prefix}/{CURRENT_VERSION}/images/test_image_bad_auth/push': + post_fake_push_bad_auth, f'{prefix}/{CURRENT_VERSION}/commit': post_fake_commit, f'{prefix}/{CURRENT_VERSION}/containers/create':