Fix delete_image: use sha256 image ID instead of name+tag
DSM SYNO.Docker.Image/delete returns error 114 when called with name+tag.
The API expects the sha256 hash from the image list (field "id") as the
"id" parameter.
- Look up the sha256 hash from SYNO.Docker.Image/list (already fetched
for the in-use check), then pass params={"id": img_hash}
- Guard against missing hash with a clear error message
- Updated tests to assert id param is sent, name/tag are absent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -243,20 +243,17 @@ def register_images(mcp: FastMCP, config: AppConfig, client: DsmClient) -> None:
|
|||||||
f"Call delete_image(image_id={image_id!r}, confirmed=True) to confirm."
|
f"Call delete_image(image_id={image_id!r}, confirmed=True) to confirm."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Perform deletion using the resolved name+tag.
|
# DSM requires the sha256 image ID for deletion, not name+tag.
|
||||||
# N4S4 reference: SYNO.Docker.Image / delete, params: name, tag
|
if not img_hash:
|
||||||
delete_name = repo
|
return f"Cannot delete '{display_name}': image ID (sha256) not found in list."
|
||||||
delete_tag = img_tags[0] if img_tags else tag
|
|
||||||
sys.stderr.write(
|
sys.stderr.write(f"[delete_image] api=SYNO.Docker.Image method=delete id={img_hash!r}\n")
|
||||||
f"[delete_image] api=SYNO.Docker.Image method=delete"
|
|
||||||
f" name={delete_name!r} tag={delete_tag!r}\n"
|
|
||||||
)
|
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
try:
|
try:
|
||||||
await client.request(
|
await client.request(
|
||||||
"SYNO.Docker.Image",
|
"SYNO.Docker.Image",
|
||||||
"delete",
|
"delete",
|
||||||
params={"name": delete_name, "tag": delete_tag},
|
params={"id": img_hash},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
code = getattr(e, "code", "?")
|
code = getattr(e, "code", "?")
|
||||||
|
|||||||
@@ -225,6 +225,13 @@ async def test_delete_image_confirmed():
|
|||||||
assert "redis:7" in result
|
assert "redis:7" in result
|
||||||
assert "freed" in result
|
assert "freed" in result
|
||||||
|
|
||||||
|
# Delete must use the sha256 id, not name+tag
|
||||||
|
delete_call = next(c for c in client.request.call_args_list if c.args[1] == "delete")
|
||||||
|
params = delete_call.kwargs.get("params") or {}
|
||||||
|
assert params.get("id") == "sha256:cccc"
|
||||||
|
assert "name" not in params
|
||||||
|
assert "tag" not in params
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_delete_image_not_found():
|
async def test_delete_image_not_found():
|
||||||
@@ -330,11 +337,12 @@ async def test_delete_image_registry_prefixed_name():
|
|||||||
assert "Deleted" in result
|
assert "Deleted" in result
|
||||||
assert "open-webui" in result
|
assert "open-webui" in result
|
||||||
|
|
||||||
# Verify delete was called with correct split params (not "ghcr" as name)
|
# Verify delete was called with the sha256 ID, not name+tag
|
||||||
delete_call = next(c for c in client.request.call_args_list if c.args[1] == "delete")
|
delete_call = next(c for c in client.request.call_args_list if c.args[1] == "delete")
|
||||||
params = delete_call.kwargs.get("params") or {}
|
params = delete_call.kwargs.get("params") or {}
|
||||||
assert params.get("name") == "ghcr.io/open-webui/open-webui"
|
assert params.get("id") == "sha256:dddd"
|
||||||
assert params.get("tag") == "v0.8.10"
|
assert "name" not in params
|
||||||
|
assert "tag" not in params
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
|||||||
Reference in New Issue
Block a user