From 31cebc66bfe5401eb1e6271ee50105d996d49fd6 Mon Sep 17 00:00:00 2001 From: Paul Berruti Date: Sun, 23 Nov 2025 19:48:02 -0800 Subject: [PATCH] fix: Handle tag@digest format in push_image method Extends the tag@digest fix to also cover push operations in docker_image.py. The push_image() method was passing combined tag@digest format directly to the Docker API's /images/{name}/push endpoint, which fails with "invalid tag format" errors. This fix: 1. Imports build_pull_arguments() into docker_image.py 2. Uses the helper in push_image() before calling the API 3. When tag contains @ (but isn't a pure digest), passes the full reference as the image name and omits the tag parameter This complements the previous fix to pull_image() methods, ensuring both pull and push operations handle tag@digest correctly. --- plugins/modules/docker_image.py | 14 ++++++++++++-- tests/unit/plugins/module_utils/test_pull_image.py | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/modules/docker_image.py b/plugins/modules/docker_image.py index 2fc62b79..18ed9dc3 100644 --- a/plugins/modules/docker_image.py +++ b/plugins/modules/docker_image.py @@ -405,6 +405,7 @@ from ansible_collections.community.docker.plugins.module_utils._image_archive im ) from ansible_collections.community.docker.plugins.module_utils._util import ( DockerBaseClass, + build_pull_arguments, clean_dict_booleans_for_docker_api, is_image_name_id, is_valid_tag, @@ -784,6 +785,10 @@ class ImageManager(DockerBaseClass): push_repository, push_tag = parse_repository_tag( push_repository ) + + # Handle combined tag@digest format - Docker API doesn't accept it in tag param + push_name, push_tag_param = build_pull_arguments(push_repository, push_tag) + push_registry, dummy = resolve_repository_name(push_repository) headers = {} header = get_config_header(self.client, push_registry) @@ -792,12 +797,17 @@ class ImageManager(DockerBaseClass): # See https://github.com/moby/moby/issues/50614. header = base64.urlsafe_b64encode(b"{}") headers["X-Registry-Auth"] = header + + params: dict[str, t.Any] = {} + if push_tag_param is not None: + params["tag"] = push_tag_param + response = self.client._post_json( - self.client._url("/images/{0}/push", push_repository), + self.client._url("/images/{0}/push", push_name), data=None, headers=headers, stream=True, - params={"tag": push_tag}, + params=params, ) self.client._raise_for_status(response) for line in self.client._stream_helper(response, decode=True): diff --git a/tests/unit/plugins/module_utils/test_pull_image.py b/tests/unit/plugins/module_utils/test_pull_image.py index 798122b8..1bb3425f 100644 --- a/tests/unit/plugins/module_utils/test_pull_image.py +++ b/tests/unit/plugins/module_utils/test_pull_image.py @@ -12,6 +12,10 @@ The Docker SDK and API don't accept "tag@digest" in the tag parameter. build_pull_arguments handles this by: - Returning full reference as name when tag contains @ - Returning None for tag in that case + +This function is used by: +- pull_image() in _common.py and _common_api.py (for pulling images) +- push_image() in docker_image.py (for pushing images) """ from __future__ import annotations