From cc9432f3faa6a90715325a4e96b8af49a384b403 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:43:51 +0100 Subject: [PATCH] Make sure that image comparison survives random order. (#1240) (#1241) (cherry picked from commit 880bc24ff087100b282fbae345953c52c921f83e) Co-authored-by: Felix Fontein --- .../targets/docker_image_tag/meta/main.yml | 1 + .../targets/docker_image_tag/tasks/main.yml | 16 +++---- .../setup_utils/filter_plugins/utils.py | 46 +++++++++++++++++++ .../targets/setup_utils/tasks/main.yml | 4 ++ 4 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 tests/integration/targets/setup_utils/filter_plugins/utils.py create mode 100644 tests/integration/targets/setup_utils/tasks/main.yml diff --git a/tests/integration/targets/docker_image_tag/meta/main.yml b/tests/integration/targets/docker_image_tag/meta/main.yml index 471ddd41..1d08a2b2 100644 --- a/tests/integration/targets/docker_image_tag/meta/main.yml +++ b/tests/integration/targets/docker_image_tag/meta/main.yml @@ -6,3 +6,4 @@ dependencies: - setup_docker - setup_docker_python_deps + - setup_utils diff --git a/tests/integration/targets/docker_image_tag/tasks/main.yml b/tests/integration/targets/docker_image_tag/tasks/main.yml index 79a2751d..628e7da9 100644 --- a/tests/integration/targets/docker_image_tag/tasks/main.yml +++ b/tests/integration/targets/docker_image_tag/tasks/main.yml @@ -76,7 +76,7 @@ - info_1.images | length == 2 - info_1.images[0].Id == pulled_images.results[0].image.Id - info_1.images[1].Id == pulled_images.results[0].image.Id - - tag_1_check == tag_1 + - tag_1_check | internal__normalize_image("image") == tag_1 | internal__normalize_image("image") - name: Tag image 1 (idempotent, check mode) docker_image_tag: @@ -102,7 +102,7 @@ - tag_2 is not changed - tag_2.diff.before == tag_2.diff.after - tag_2.diff.before.images | length == 2 - - tag_2_check == tag_2 + - tag_2_check | internal__normalize_image("image") == tag_2 | internal__normalize_image("image") - name: Tag image 1 (idempotent, different input format, check mode) docker_image_tag: @@ -130,7 +130,7 @@ - tag_3 is not changed - tag_3.diff.before == tag_3.diff.after - tag_3.diff.before.images | length == 2 - - tag_3_check == tag_3 + - tag_3_check | internal__normalize_image("image") == tag_3 | internal__normalize_image("image") - name: Tag image 1 (one more, check mode) docker_image_tag: @@ -173,7 +173,7 @@ - info_4.images[0].Id == pulled_images.results[0].image.Id - info_4.images[1].Id == pulled_images.results[0].image.Id - info_4.images[2].Id == pulled_images.results[0].image.Id - - tag_4_check == tag_4 + - tag_4_check | internal__normalize_image("image") == tag_4 | internal__normalize_image("image") - name: Tag image 2 (only change missing one, check mode) docker_image_tag: @@ -219,7 +219,7 @@ - info_5.images[1].Id == pulled_images.results[0].image.Id - info_5.images[2].Id == pulled_images.results[1].image.Id - info_5.images[3].Id == pulled_images.results[0].image.Id - - tag_5_check == tag_5 + - tag_5_check | internal__normalize_image("image") == tag_5 | internal__normalize_image("image") - name: Tag image 2 (idempotent, check mode) docker_image_tag: @@ -249,7 +249,7 @@ - tag_6 is not changed - tag_6.diff.before == tag_6.diff.after - tag_6.diff.before.images | length == 3 - - tag_6_check == tag_6 + - tag_6_check | internal__normalize_image("image") == tag_6 | internal__normalize_image("image") - name: Tag image 2 (only change wrong ones, check mode) docker_image_tag: @@ -296,7 +296,7 @@ - info_7.images[1].Id == pulled_images.results[1].image.Id - info_7.images[2].Id == pulled_images.results[1].image.Id - info_7.images[3].Id == pulled_images.results[0].image.Id - - tag_7_check == tag_7 + - tag_7_check | internal__normalize_image("image") == tag_7 | internal__normalize_image("image") - name: Tag image 2 (idempotent, check mode) docker_image_tag: @@ -326,7 +326,7 @@ - tag_8 is not changed - tag_8.diff.before == tag_8.diff.after - tag_8.diff.before.images | length == 3 - - tag_8_check == tag_8 + - tag_8_check | internal__normalize_image("image") == tag_8 | internal__normalize_image("image") - name: Tag image 3 (source image has digest) docker_image_tag: diff --git a/tests/integration/targets/setup_utils/filter_plugins/utils.py b/tests/integration/targets/setup_utils/filter_plugins/utils.py new file mode 100644 index 00000000..4736350a --- /dev/null +++ b/tests/integration/targets/setup_utils/filter_plugins/utils.py @@ -0,0 +1,46 @@ +# Copyright (c) 2026, Felix Fontein +# 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 + +from __future__ import annotations + +from collections.abc import Mapping, Sequence + + +def _normalize_image_impl(data): + identity = data.get("Identity") + if not isinstance(identity, Mapping): + return data + pull = identity.get("Pull") + if not isinstance(pull, Sequence): + return data + new_identity = dict(identity) + new_identity["Pull"] = sorted(identity["Pull"], key=lambda entry: entry.get("Repository") or "") + new_data = dict(data) + new_data["Identity"] = new_identity + return new_data + + +def _process(data, place_parts, place_index, processor): + if place_index == len(place_parts): + return processor(data) + new_data = dict(data) + new_data[place_parts[place_index]] = _process(data[place_parts[place_index]], place_parts, place_index + 1, processor) + return new_data + + +def normalize_image(data, *places): + if not places: + return _normalize_image_impl(data) + for place in places: + data = _process(data, place.split("."), 0, _normalize_image_impl) + return data + + +class FilterModule: + """Utilities for community.docker tests.""" + + def filters(self): + return { + 'internal__normalize_image': normalize_image, + } diff --git a/tests/integration/targets/setup_utils/tasks/main.yml b/tests/integration/targets/setup_utils/tasks/main.yml new file mode 100644 index 00000000..f55df21f --- /dev/null +++ b/tests/integration/targets/setup_utils/tasks/main.yml @@ -0,0 +1,4 @@ +--- +# 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