From b3723a30c3d33292308a48f93e2fe6516aad890e Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Fri, 14 Nov 2025 19:54:25 +0100 Subject: [PATCH] Fix idempotency problem for IPv6 addresses. --- .../fragments/1192-docker_container-pull.yml | 1 + .../module_utils/_module_container/module.py | 13 ++++++----- plugins/module_utils/_util.py | 23 +++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/changelogs/fragments/1192-docker_container-pull.yml b/changelogs/fragments/1192-docker_container-pull.yml index 06ad0f19..f152064a 100644 --- a/changelogs/fragments/1192-docker_container-pull.yml +++ b/changelogs/fragments/1192-docker_container-pull.yml @@ -1,2 +1,3 @@ bugfixes: - "docker_container - fix ``pull`` idempotency with Docker 29.0.0 (https://github.com/ansible-collections/community.docker/pull/1192)." + - "docker_container - fix idempotency for IPv6 addresses with Docker 29.0.0 (https://github.com/ansible-collections/community.docker/pull/1192)." diff --git a/plugins/module_utils/_module_container/module.py b/plugins/module_utils/_module_container/module.py index 75128cfe..e9f10cd8 100644 --- a/plugins/module_utils/_module_container/module.py +++ b/plugins/module_utils/_module_container/module.py @@ -25,6 +25,7 @@ from ansible_collections.community.docker.plugins.module_utils._util import ( DockerBaseClass, compare_generic, is_image_name_id, + normalize_ip_address, sanitize_result, ) @@ -925,13 +926,13 @@ class ContainerManager(DockerBaseClass, t.Generic[Client]): else: diff = False network_info_ipam = network_info.get("IPAMConfig") or {} - if network.get("ipv4_address") and network[ - "ipv4_address" - ] != network_info_ipam.get("IPv4Address"): + if network.get("ipv4_address") and normalize_ip_address( + network["ipv4_address"] + ) != normalize_ip_address(network_info_ipam.get("IPv4Address")): diff = True - if network.get("ipv6_address") and network[ - "ipv6_address" - ] != network_info_ipam.get("IPv6Address"): + if network.get("ipv6_address") and normalize_ip_address( + network["ipv6_address"] + ) != normalize_ip_address(network_info_ipam.get("IPv6Address")): diff = True if network.get("aliases") and not compare_generic( network["aliases"], diff --git a/plugins/module_utils/_util.py b/plugins/module_utils/_util.py index 2ca60cac..45452a01 100644 --- a/plugins/module_utils/_util.py +++ b/plugins/module_utils/_util.py @@ -7,6 +7,7 @@ from __future__ import annotations +import ipaddress import json import re import typing as t @@ -505,3 +506,25 @@ def omit_none_from_dict(d: dict[str, t.Any]) -> dict[str, t.Any]: Return a copy of the dictionary with all keys with value None omitted. """ return {k: v for (k, v) in d.items() if v is not None} + + +@t.overload +def normalize_ip_address(ip_address: str) -> str: ... + + +@t.overload +def normalize_ip_address(ip_address: str | None) -> str | None: ... + + +def normalize_ip_address(ip_address: str | None) -> str | None: + """ + Given an IP address as a string, normalize it so that it can be + used to compare IP addresses as strings. + """ + if ip_address is None: + return None + try: + return ipaddress.ip_address(ip_address).compressed + except ValueError: + # Fallback for invalid addresses: simply return the input + return ip_address