From d5d7e65a6a0d34b3c292c0ec6488473614c081c6 Mon Sep 17 00:00:00 2001 From: spatterlight Date: Thu, 19 Mar 2026 19:39:40 -0400 Subject: [PATCH 1/7] docker_image_export: Add 'platform' option docker_image_export: Add 'platform' option --- .../1064-docker-image-export-platform.yml | 2 + plugins/modules/docker_image_export.py | 41 ++++++++++++++++++- .../tasks/tests/platform.yml | 34 +++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 changelogs/fragments/1064-docker-image-export-platform.yml create mode 100644 tests/integration/targets/docker_image_export/tasks/tests/platform.yml diff --git a/changelogs/fragments/1064-docker-image-export-platform.yml b/changelogs/fragments/1064-docker-image-export-platform.yml new file mode 100644 index 00000000..f7b2ec7f --- /dev/null +++ b/changelogs/fragments/1064-docker-image-export-platform.yml @@ -0,0 +1,2 @@ +minor_changes: + - docker_image_export - adds ``platform`` parameter to allow exporting a specific platform variant from a multi-arch image (https://github.com/ansible-collections/community.docker/issues/1064). diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index b5cc10f3..b5f2b75a 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -61,6 +61,13 @@ options: - Export the image even if the C(.tar) file already exists and seems to contain the right image. type: bool default: false + platform: + description: + - Ask for this specific platform when exporting. + - For example, C(linux/amd64), C(linux/arm64/v8). + - Requires Docker API 1.48 or newer. + type: str + version_added: "5.1.0" requirements: - "Docker API >= 1.25" @@ -98,6 +105,7 @@ images: sample: [] """ +import json import traceback import typing as t @@ -110,6 +118,9 @@ from ansible_collections.community.docker.plugins.module_utils._api.errors impor from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( parse_repository_tag, ) +from ansible_collections.community.docker.plugins.module_utils._platform import ( + _Platform, +) from ansible_collections.community.docker.plugins.module_utils._common_api import ( AnsibleDockerClient, RequestException, @@ -137,6 +148,7 @@ class ImageExportManager(DockerBaseClass): self.path = parameters["path"] self.force = parameters["force"] self.tag = parameters["tag"] + self.platform = parameters["platform"] if not is_valid_tag(self.tag, allow_empty=True): self.fail(f'"{self.tag}" is not a valid docker tag') @@ -198,15 +210,31 @@ class ImageExportManager(DockerBaseClass): except Exception as exc: # pylint: disable=broad-exception-caught self.fail(f"Error writing image archive {self.path} - {exc}") + def _platform_param(self) -> str: + platform = _Platform.parse_platform_string(self.platform) + platform_spec: dict[str, str] = {} + if platform.os: + platform_spec["os"] = platform.os + if platform.arch: + platform_spec["architecture"] = platform.arch + if platform.variant: + platform_spec["variant"] = platform.variant + return json.dumps(platform_spec) + def export_images(self) -> None: image_names = [name["joined"] for name in self.names] image_names_str = ", ".join(image_names) if len(image_names) == 1: self.log(f"Getting archive of image {image_names[0]}") + params: dict[str, t.Any] = {} + if self.platform: + params["platform"] = self._platform_param() try: chunks = self.client._stream_raw_result( self.client._get( - self.client._url("/images/{0}/get", image_names[0]), stream=True + self.client._url("/images/{0}/get", image_names[0]), + stream=True, + params=params, ), chunk_size=DEFAULT_DATA_CHUNK_SIZE, decode=False, @@ -215,12 +243,15 @@ class ImageExportManager(DockerBaseClass): self.fail(f"Error getting image {image_names[0]} - {exc}") else: self.log(f"Getting archive of images {image_names_str}") + params: dict[str, t.Any] = {"names": image_names} + if self.platform: + params["platform"] = self._platform_param() try: chunks = self.client._stream_raw_result( self.client._get( self.client._url("/images/get"), stream=True, - params={"names": image_names}, + params=params, ), chunk_size=DEFAULT_DATA_CHUNK_SIZE, decode=False, @@ -277,11 +308,17 @@ def main() -> None: "aliases": ["name"], }, "tag": {"type": "str", "default": "latest"}, + "platform": {"type": "str"}, + } + + option_minimal_versions = { + "platform": {"docker_api_version": "1.48"}, } client = AnsibleDockerClient( argument_spec=argument_spec, supports_check_mode=True, + option_minimal_versions=option_minimal_versions, ) try: diff --git a/tests/integration/targets/docker_image_export/tasks/tests/platform.yml b/tests/integration/targets/docker_image_export/tasks/tests/platform.yml new file mode 100644 index 00000000..76a42b43 --- /dev/null +++ b/tests/integration/targets/docker_image_export/tasks/tests/platform.yml @@ -0,0 +1,34 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- when: docker_api_version is version('1.48', '>=') + block: + - name: Pull image for platform test + community.docker.docker_image_pull: + name: "{{ docker_test_image_hello_world }}" + platform: linux/amd64 + + - name: Export image with platform (check mode) + community.docker.docker_image_export: + name: "{{ docker_test_image_hello_world }}" + path: "{{ remote_tmp_dir }}/platform-test.tar" + platform: linux/amd64 + register: result_check + check_mode: true + + - ansible.builtin.assert: + that: + - result_check is changed + + - name: Export image with platform + community.docker.docker_image_export: + name: "{{ docker_test_image_hello_world }}" + path: "{{ remote_tmp_dir }}/platform-test.tar" + platform: linux/amd64 + register: result + + - ansible.builtin.assert: + that: + - result is changed From 5a28a4e346ecd59805e9e12d6f6d238043c1890e Mon Sep 17 00:00:00 2001 From: spatterlight Date: Mon, 23 Mar 2026 14:51:28 -0400 Subject: [PATCH 2/7] docker_image_export: Add 'platform' option --- plugins/modules/docker_image_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index b5f2b75a..5dcb7d97 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -64,7 +64,7 @@ options: platform: description: - Ask for this specific platform when exporting. - - For example, C(linux/amd64), C(linux/arm64/v8). + - For example, C(linux/amd64), C(linux/arm64). - Requires Docker API 1.48 or newer. type: str version_added: "5.1.0" From 908680d329cbb3f898459301d8dcd91f32b1bd9c Mon Sep 17 00:00:00 2001 From: spatterlight <81454789+spatterIight@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:03:21 -0400 Subject: [PATCH 3/7] Update changelogs/fragments/1064-docker-image-export-platform.yml Co-authored-by: Felix Fontein --- changelogs/fragments/1064-docker-image-export-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/fragments/1064-docker-image-export-platform.yml b/changelogs/fragments/1064-docker-image-export-platform.yml index f7b2ec7f..3ff9378d 100644 --- a/changelogs/fragments/1064-docker-image-export-platform.yml +++ b/changelogs/fragments/1064-docker-image-export-platform.yml @@ -1,2 +1,2 @@ minor_changes: - - docker_image_export - adds ``platform`` parameter to allow exporting a specific platform variant from a multi-arch image (https://github.com/ansible-collections/community.docker/issues/1064). + - docker_image_export - adds ``platform`` parameter to allow exporting a specific platform variant from a multi-arch image (https://github.com/ansible-collections/community.docker/issues/1064, https://github.com/ansible-collections/community.docker/pull/1251). From d25b4c6097a384937bb953f710409a3dde16d09b Mon Sep 17 00:00:00 2001 From: spatterlight <81454789+spatterIight@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:03:32 -0400 Subject: [PATCH 4/7] Update plugins/modules/docker_image_export.py Co-authored-by: Felix Fontein --- plugins/modules/docker_image_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index 5dcb7d97..7840e583 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -67,7 +67,7 @@ options: - For example, C(linux/amd64), C(linux/arm64). - Requires Docker API 1.48 or newer. type: str - version_added: "5.1.0" + version_added: 5.2.0 requirements: - "Docker API >= 1.25" From 84719fb753002fde9a96484200c28cf9cf665daf Mon Sep 17 00:00:00 2001 From: spatterlight Date: Mon, 23 Mar 2026 16:07:43 -0400 Subject: [PATCH 5/7] docker_image_export: Add 'platform' option --- plugins/modules/docker_image_export.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index 7840e583..0c8f9ca2 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -118,9 +118,6 @@ from ansible_collections.community.docker.plugins.module_utils._api.errors impor from ansible_collections.community.docker.plugins.module_utils._api.utils.utils import ( parse_repository_tag, ) -from ansible_collections.community.docker.plugins.module_utils._platform import ( - _Platform, -) from ansible_collections.community.docker.plugins.module_utils._common_api import ( AnsibleDockerClient, RequestException, @@ -130,6 +127,9 @@ from ansible_collections.community.docker.plugins.module_utils._image_archive im api_image_id, load_archived_image_manifest, ) +from ansible_collections.community.docker.plugins.module_utils._platform import ( + _Platform, +) from ansible_collections.community.docker.plugins.module_utils._util import ( DockerBaseClass, is_image_name_id, From f536b8967b1bb1c6976eb480704d11165e9de0b3 Mon Sep 17 00:00:00 2001 From: spatterlight Date: Mon, 23 Mar 2026 16:21:21 -0400 Subject: [PATCH 6/7] docker_image_export: Add 'platform' option --- plugins/modules/docker_image_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index 0c8f9ca2..241d4b6e 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -243,7 +243,7 @@ class ImageExportManager(DockerBaseClass): self.fail(f"Error getting image {image_names[0]} - {exc}") else: self.log(f"Getting archive of images {image_names_str}") - params: dict[str, t.Any] = {"names": image_names} + params = {"names": image_names} if self.platform: params["platform"] = self._platform_param() try: From 19525888dac2c23ecf57ddea0da3c662b7fedd42 Mon Sep 17 00:00:00 2001 From: spatterlight Date: Thu, 26 Mar 2026 13:37:49 -0400 Subject: [PATCH 7/7] docker_image_export: Add 'platform' option --- plugins/modules/docker_image_export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/modules/docker_image_export.py b/plugins/modules/docker_image_export.py index 241d4b6e..9a3ddee5 100644 --- a/plugins/modules/docker_image_export.py +++ b/plugins/modules/docker_image_export.py @@ -31,9 +31,12 @@ attributes: details: - Whether the module is idempotent depends on the storage API used for images, which determines how the image ID is computed. The idempotency check needs - that the image ID equals the ID stored in archive's C(manifest.json). + the image ID to equal the ID stored in archive's C(manifest.json). This seemed to have worked fine with the default storage backend up to Docker 28, but seems to have changed in Docker 29. + - This module is B(not idempotent) when used with multi-architecture images, + regardless of Docker version. + - Full idempotency requires Docker 28 or earlier B(and) a single-architecture image. options: names: