Commit Graph

7 Commits

Author SHA1 Message Date
Paul Berruti
0525a802bb fix: Match by digest only for combined tag@digest image lookups
When using combined tag@digest references (e.g., nginx:1.21@sha256:abc...),
Docker does NOT store the tag in RepoTags. It only stores the digest in
RepoDigests. The previous implementation required BOTH to match, which
always failed because RepoTags was empty.

This caused docker_container to pull the image on every run even when
the image with the correct digest already existed locally, breaking
idempotency.

The fix: When a digest is specified, match by digest only since it's the
authoritative identifier. The tag is informational for human readability.

Real-world example from docker image inspect:
  "RepoTags": [],  # Empty when pulled by digest\!
  "RepoDigests": ["portainer/portainer-ee@sha256:7ecf2008..."]

Updated tests to reflect the correct behavior:
- test_empty_repo_tags_matches_by_digest (the critical fix case)
- test_combined_tag_digest_matches_even_if_tag_differs
- test_multiple_tags_irrelevant_for_combined
2025-11-23 21:40:32 -08:00
Paul Berruti
21288cda7e Fix _image_lookup to handle combined tag@digest format
The parse_repository_tag fix alone is not sufficient because Docker stores
RepoTags and RepoDigests separately. When looking up an image with combined
tag@digest (e.g., nginx:1.21@sha256:abc...), the _image_lookup function must
split the combined format and match BOTH RepoTags (for the tag) AND
RepoDigests (for the digest).

Docker stores:
- RepoTags: ["nginx:1.21"]
- RepoDigests: ["nginx@sha256:abc..."]

But NEVER stores the combined format. The previous code would construct:
- lookup = "nginx:1.21@sha256:abc..." (never matches RepoTags)
- lookup_digest = "nginx@1.21@sha256:abc..." (never matches RepoDigests)

This fix:
1. Adds filter_images_by_tag() helper function to _util.py to avoid code
   duplication between _common.py and _common_api.py
2. Detects combined tag@digest format in the tag parameter
3. Splits into tag_part and digest_part
4. Constructs proper lookups for both RepoTags and RepoDigests
5. Requires BOTH to match for successful image lookup

Without this fix, image_label_mismatch: ignore fails because the image
cannot be found, resulting in no image labels being included in expected
labels comparison.

Includes comprehensive unit tests in test__util.py covering all scenarios
including edge cases for multiple @ symbols and empty tag parts.
2025-11-22 20:10:21 -08:00
Felix Fontein
6ad4bfcd40
Add typing information, 2/n (#1178)
* Add typing to Docker Stack modules. Clean modules up.

* Add typing to Docker Swarm modules.

* Add typing to unit tests.

* Add more typing.

* Add ignore.txt entries.
2025-10-25 01:16:04 +02:00
Felix Fontein
892e9d9cbd Reorganize imports due to https://github.com/ansible-community/antsibull-nox/pull/136. 2025-10-10 21:19:28 +02:00
Felix Fontein
e8ec22d3b1
Python code modernization, 3/n (#1157)
* Remove __metaclass__ = type.

for i in $(grep -REl '__metaclass__ = type' plugins/ tests/); do
  sed -e '/^__metaclass__ = type/d' -i $i;
done

* Remove super arguments, and stop inheriting from object.
2025-10-10 08:11:58 +02:00
Felix Fontein
741c318b1d
Python code modernization, 2/n (#1156)
* Adjust all __future__ imports:

for i in $(grep -REl "__future__.*absolute_import" plugins/ tests/); do
  sed -e 's/from __future__ import .*/from __future__ import annotations/g' -i $i;
done

* Remove all UTF-8 encoding specifications for Python source files:

for i in $(grep -REl '[-][*]- coding: utf-8 -[*]-' plugins/ tests/); do
  sed -e '/^# -\*- coding: utf-8 -\*-/d' -i $i;
done

* Reformat.
2025-10-09 20:46:48 +02:00
Felix Fontein
117271579e
Make all doc fragments, module utils, and plugin utils private (#1144)
* Make all doc fragments, module utils, and plugin utils private.

* Remove some unused and no longer needed imports.

This hopefully also fixes the CI issues, which do not happen locally for me...

* Fix formatting.

* Try to make CI happy, again.

* Fix imports.

* Lint.
2025-10-07 07:32:33 +02:00