1
0
Fork 0
mirror of https://github.com/containers/ansible-podman-collections.git synced 2026-02-04 07:11:49 +00:00

Fix image idempotency in pull

Fix #981
Signed-off-by: Sagi Shnaidman <sshnaidm@redhat.com>
This commit is contained in:
Sagi Shnaidman 2025-10-19 12:02:11 +03:00
parent cf7008fb0e
commit 6988297262
3 changed files with 94 additions and 4 deletions

View file

@ -569,11 +569,27 @@ class PodmanImageManager:
"""Ensure image is present (pull or build if needed)."""
image = self.find_image()
# Get digest before any operations
digest_before = None
if image:
inspect_data = self.inspector.inspect_image(self.repository.full_name)
if inspect_data and len(inspect_data) > 0:
digest_before = inspect_data[0].get("Digest") or inspect_data[0].get("digest")
# If Digest is not available, try to get from RepoDigests
if not digest_before:
repo_digests = inspect_data[0].get("RepoDigests", [])
if repo_digests:
# Extract digest from first RepoDigest (format: repo@sha256:...)
for repo_digest in repo_digests:
if "@" in repo_digest:
digest_before = repo_digest.split("@", 1)[1]
break
if not image or self._should_rebuild_image(image):
if self.params.get("state") == "build" or self.params.get("path"):
self._build_image()
else:
self._pull_image()
self._pull_image(digest_before)
if self.params.get("push"):
self._push_image()
@ -611,7 +627,7 @@ class PodmanImageManager:
self.results["changed"] = True
self.results["actions"].append(f"Built image {self.repository.full_name} from {path or 'context'}")
def _pull_image(self):
def _pull_image(self, digest_before=None):
"""Pull an image."""
if not self.params.get("pull", True):
self.module.fail_json(msg=f"Image {self.repository.full_name} not found locally and pull is disabled")
@ -623,8 +639,27 @@ class PodmanImageManager:
self.results["image"] = self.inspector.inspect_image(self.repository.full_name)
self.results["podman_actions"].append(podman_command)
self.results["changed"] = True
self.results["actions"].append(f"Pulled image {self.repository.full_name}")
# Check if digest actually changed
digest_after = None
if self.results["image"] and len(self.results["image"]) > 0:
digest_after = self.results["image"][0].get("Digest") or self.results["image"][0].get("digest")
# If Digest is not available, try to get from RepoDigests
if not digest_after:
repo_digests = self.results["image"][0].get("RepoDigests", [])
if repo_digests:
# Extract digest from first RepoDigest (format: repo@sha256:...)
for repo_digest in repo_digests:
if "@" in repo_digest:
digest_after = repo_digest.split("@", 1)[1]
break
changed = digest_before != digest_after
self.results["changed"] = changed
if changed:
self.results["actions"].append(f"Pulled image {self.repository.full_name}")
else:
self.results["changed"] = True
self.results["actions"].append(f"Pulled image {self.repository.full_name}")
def _push_image(self):
"""Push an image."""

View file

@ -589,6 +589,7 @@
- include_tasks: additional_tests.yml
- include_tasks: test_issue_947.yml
- include_tasks: test_issue_981.yml
always:
- name: Cleanup images

View file

@ -0,0 +1,54 @@
---
# Test for issue #981: podman_image with force=true should be idempotent
# https://github.com/containers/ansible-podman-collections/issues/981
- name: Test issue # 981 - Remove alpine image if exists
containers.podman.podman_image:
executable: "{{ test_executable | default('podman') }}"
name: docker.io/library/alpine
tag: latest
state: absent
ignore_errors: true
- name: Test issue # 981 - Pull alpine image first time
containers.podman.podman_image:
executable: "{{ test_executable | default('podman') }}"
name: docker.io/library/alpine
tag: latest
register: issue_981_pull1
- name: Test issue # 981 - Pull alpine with force=true (same digest, should not change)
containers.podman.podman_image:
executable: "{{ test_executable | default('podman') }}"
name: docker.io/library/alpine
tag: latest
force: true
register: issue_981_pull2
- name: Test issue # 981 - Pull alpine with force=true again (same digest, should not change)
containers.podman.podman_image:
executable: "{{ test_executable | default('podman') }}"
name: docker.io/library/alpine
tag: latest
force: true
register: issue_981_pull3
- name: Test issue # 981 - Verify force=true idempotency
assert:
that:
- issue_981_pull1 is changed
- issue_981_pull1.actions | length > 0
- "'Pulled image' in issue_981_pull1.actions[0]"
- issue_981_pull2 is not changed
- issue_981_pull2.actions | length == 0
- issue_981_pull3 is not changed
- issue_981_pull3.actions | length == 0
fail_msg: "Issue #981 not fixed: force=true is not idempotent when digest hasn't changed"
success_msg: "Issue #981 fixed: force=true is idempotent when digest hasn't changed"
- name: Test issue # 981 - Cleanup
containers.podman.podman_image:
executable: "{{ test_executable | default('podman') }}"
name: docker.io/library/alpine
tag: latest
state: absent