fix: compat API image push err meg propagation

When returning error http code (e.g. 4xx, 5xx) the body needs to contain
a JSON that has a key "message" in it,
we must not use jsonmessage.JSONMessage.

The JSON of shape jsonmessage.JSONMessage is used only when client
already received 200.

Signed-off-by: Matej Vašek <matejvasek@gmail.com>
This commit is contained in:
Matej Vašek 2026-03-09 18:43:56 +01:00
parent d1f0833c5f
commit db70e73056
No known key found for this signature in database
GPG key ID: 72036E3FC07BF8F1
3 changed files with 39 additions and 7 deletions

View file

@ -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,
}

View file

@ -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

View file

@ -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()