diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go index 17bf1eb0cd..3306b6de45 100644 --- a/pkg/api/handlers/compat/images_push.go +++ b/pkg/api/handlers/compat/images_push.go @@ -189,15 +189,18 @@ loop: // break out of for/select infinite loop flush() case err := <-pushErrChan: if err != nil { - var msg string + code := http.StatusInternalServerError if errors.Is(err, storage.ErrImageUnknown) { // Image may have been removed in the meantime. - writeStatusCode(http.StatusNotFound) - msg = "An image does not exist locally with the tag: " + imageName - } else { - writeStatusCode(http.StatusInternalServerError) - msg = err.Error() + code = http.StatusNotFound + err = errors.New("An image does not exist locally with the tag: " + imageName) } + if !statusWritten { + utils.Error(w, code, err) + flush() + break loop + } + var msg = err.Error() report.Error = &jsonstream.Error{ Message: msg, } diff --git a/test/apiv2/12-imagesMore.at b/test/apiv2/12-imagesMore.at index e7ebc98d14..c35be5860f 100644 --- a/test/apiv2/12-imagesMore.at +++ b/test/apiv2/12-imagesMore.at @@ -26,7 +26,7 @@ t GET libpod/images/$IMAGE/json 200 \ # Push to local registry... t POST "/v1.40/images/localhost:$REGISTRY_PORT/myrepo/push?tag=mytag" 500 \ - .error~".*x509: certificate signed by unknown authority" + .message~".*x509: certificate signed by unknown authority" t POST "images/localhost:$REGISTRY_PORT/myrepo/push?tlsVerify=false&tag=mytag" 200 \ .error~null diff --git a/test/python/docker/compat/test_images.py b/test/python/docker/compat/test_images.py index b1294467c3..8c7317eec3 100644 --- a/test/python/docker/compat/test_images.py +++ b/test/python/docker/compat/test_images.py @@ -5,8 +5,10 @@ import io import os import unittest import json +from typing import Iterator from docker import errors +from docker.errors import APIError # pylint: disable=no-name-in-module,import-error,wrong-import-order from test.python.docker.compat import common, constant @@ -135,6 +137,33 @@ class TestImages(common.DockerTestCase): raise IOError(f"Line '{line}' was not JSON parsable") assert "errorDetail" not in parsed + def test_push_error(self): + """Validates that push error is correctly propagated""" + alpine = self.docker.images.get(constant.ALPINE) + if not alpine.tag("non-existent.lan:5000/alpine", "latest"): + self.fail("not tagged") + try: + resp: str = self.docker.images.push("non-existent.lan:5000/alpine", "latest") + for obj in json_stream(resp): + if 'no such host' in obj.get('errorDetail', {}).get('message', ''): + return + self.fail('expected error not found in the response') + except APIError as e: + if 'no such host' not in e.explanation: + self.fail('expected error not found in the response') + +def json_stream(data: str) -> Iterator[dict]: + decoder = json.JSONDecoder() + pos = 0 + length = len(data) + while pos < length: + if data[pos].isspace(): + pos += 1 + continue + obj, pos_end = decoder.raw_decode(data, pos) + yield obj + pos = pos_end + if __name__ == "__main__": # Setup temporary space unittest.main()