Fix DockerImageName compatibility check when digest is present#11629
Conversation
…ontainers#10527) Strip tag from repository when parsing images with both tag and digest (e.g., postgres:16.8@sha256:...) to prevent tag leaking into repository name.
|
@codex review |
|
Codex Review: Didn't find any major issues. Another round soon, please! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Thanks for your contribution @konstantinosGkilas. It seems, we would still have issues with that rely on DockerImageName image = DockerImageName.parse(
"confluentinc/cp-kafka:7.3.0@sha256:1234abcd1234abcd1234abcd1234abcd"
);
KafkaContainer container = new KafkaContainer(image).withKraft();But I don't think this needs to block this PR, since it is already an improvement over the status quo. Note that CI is failing for linting issues, so unfortunately you have to run |
When parsing image names like "image:7.3.0@sha256:abcd", the tag was previously discarded. This caused getVersionPart() to return the sha256 digest instead of the tag, breaking version-based feature checks in modules like Kafka, Neo4j, Elasticsearch, etc. Now getVersionPart() returns the tag when both are present, and a new getDigest() method provides access to the sha256 digest. RemoteDockerImage uses the digest for pulling when available. asCanonicalNameString() correctly outputs "image:tag@sha256:hash" format.
|
Thanks for the review @kiview ! I've addressed the linting issues and also took a stab at the getVersionPart() limitation you mentioned. When both tag and digest are present (e.g. confluentinc/cp-kafka:7.3.0@sha256:…), getVersionPart() now returns the tag (7.3.0) instead of the digest, so version-based checks in modules like Kafka, Elasticsearch, Neo4j, etc. should work correctly. A new getDigest() method provides access to the sha256 when needed, and RemoteDockerImage uses it for pulling.asCanonicalNameString() also outputs the full image:tag@sha256:hash format. I could not locate any other open issue, also affected by this change positively. If you have something in mind please feel free to reference it as well. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6da85e7f45
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| String beforeDigest = remoteName.split("@sha256:")[0]; | ||
| if (beforeDigest.contains(":")) { | ||
| repository = beforeDigest.split(":")[0]; | ||
| String tag = beforeDigest.split(":")[1]; |
There was a problem hiding this comment.
Handle empty tag before digest parsing
This branch assumes beforeDigest.split(":")[1] always exists, but Java drops trailing empty segments, so an input like repo:@sha256:... throws ArrayIndexOutOfBoundsException during parse instead of being rejected cleanly by assertValid(). That turns a user-facing validation error into an unexpected runtime crash path for malformed image names.
Useful? React with 👍 / 👎.
| Sha256Versioning(String hash, String tag) { | ||
| this.hash = hash; | ||
| this.tag = tag; | ||
| } |
There was a problem hiding this comment.
Validate tag syntax for tag+digest inputs
After introducing the optional tag field on Sha256Versioning, DockerImageName.parse("name:tag@sha256:...") now stores the tag there, but Sha256Versioning.isValid() still validates only the hash. This means malformed tags (for example name:-bad@sha256:...) can pass assertValid(), which is a regression in image-name validation and can defer failures to later pull operations.
Useful? React with 👍 / 👎.
|
Thanks for looking at the versioning part @konstantinosGkilas. Malformed tags with sha present will now be considered valid and normalized: DockerImageName
.parse("repo/image:tag:extra@sha256:1234abcd1234abcd1234abcd1234abcd")
.assertValid();Normalized to: Also, I don't think we should change the hashCode/equals contract here, might create some confusion to users. DockerImageName a = DockerImageName.parse(
"repo/image:1.0@sha256:1234abcd1234abcd1234abcd1234abcd"
);
DockerImageName b = DockerImageName.parse(
"repo/image:latest@sha256:1234abcd1234abcd1234abcd1234abcd"
); |
I will have another look probably with a small change the image parsing/evaluation should work. Also https://github.com/testcontainers/testcontainers-java/actions/runs/23963765698/job/69959157481?pr=11629#logs seems to have issues besides my PR. |
|
Yep, that run looked like some networking glitched, triggered a re-run. |
- Use split(":", 2) to preserve full tag content instead of silently
truncating at extra colons
- Remove @EqualsAndHashCode.Exclude from tag field so images with
different tags are not incorrectly equal
- Validate tag against TAG_REGEX in Sha256Versioning.isValid()
- Add test cases for malformed tag rejection and tag-based equality
|
@kiview
Then addressed them with three targeted fixes:
|
Fixes #10527
Summary
DockerImageNamewhere images containing both a tag and a digest (e.g.,postgres:16.8@sha256:...) had the tag leak into the repository name, causingisCompatibleWith()to incorrectly fail@sha256:so that the repository is correctly extractedContext
The bug originates from
DockerImageName's constructor parsing logic at line 90, where images containing both a tag and a digest (e.g.,postgres:16.8@sha256:301bcb...) have theremoteNamesplit only on@sha256:, leaving the tag embedded in the repository (postgres:16.8instead ofpostgres). This causesisCompatibleWith()to fail because the repository comparison becomes"postgres:16.8".equals("postgres"). While downstream projects like kroxylicious have worked around this by explicitly declaring compatibility viaasCompatibleSubstituteFor(), the fix should reside in testcontainers-java itself so that digest-pinned images are parsed correctly without requiring manual workarounds.Test plan
DockerImageNameTestandDockerImageNameCompatibilityTestpassisCompatibleWith()succeeds for digest-only, tag+digest, and registry/tag+digest images