@@ -1539,8 +1539,29 @@ async def docker_exapp_image_remove(request: web.Request):
15391539 try :
15401540 async with session .delete (delete_url ) as resp :
15411541 if resp .status == 200 :
1542- LOGGER .info ("Image '%s' removed (%d bytes freed)." , image_ref , bytes_freed )
1543- return web .json_response ({"deleted" : True , "bytes_freed" : bytes_freed })
1542+ # Docker returns a JSON array describing what happened, e.g.
1543+ # [{"Untagged": "..."}, {"Deleted": "sha256:..."}]
1544+ # If the image had more than one tag, only the requested tag is
1545+ # untagged and the underlying image stays on disk. In that case
1546+ # no bytes were actually freed even though Docker says 200, so we
1547+ # report bytes_freed=0 instead of the inspected size — otherwise
1548+ # multi-tagged images would inflate the "space reclaimed" numbers
1549+ # admins see in the logs.
1550+ try :
1551+ ops = await resp .json ()
1552+ except (json .JSONDecodeError , aiohttp .ContentTypeError ):
1553+ ops = []
1554+ actually_deleted = isinstance (ops , list ) and any (
1555+ isinstance (op , dict ) and "Deleted" in op for op in ops
1556+ )
1557+ actual_bytes_freed = bytes_freed if actually_deleted else 0
1558+ LOGGER .info (
1559+ "Image '%s' removed (deleted=%s, bytes_freed=%d)." ,
1560+ image_ref ,
1561+ actually_deleted ,
1562+ actual_bytes_freed ,
1563+ )
1564+ return web .json_response ({"deleted" : True , "bytes_freed" : actual_bytes_freed })
15441565 if resp .status == 404 :
15451566 LOGGER .info ("Image '%s' already gone at delete time." , image_ref )
15461567 return web .json_response ({"deleted" : True , "bytes_freed" : 0 , "reason" : "not_found" })
0 commit comments