Your deployment pins myorg/serving:^1.4.0 rather than :latest, and the registry holds a pile of tags — release versions, release candidates, latest, dev. Resolve the constraint to the single tag that should actually be pulled.
An image tag is a string, so the registry has no idea 1.10.0 is newer than 1.9.0 — sorted as text, "1.10.0" < "1.9.0". Resolution has to parse the tag into numeric components first. That off-by-one-release bug is a classic way to deploy a stale image.
Constraint grammar for this problem:
| Constraint | Meaning | |---|---| | ***** | any release tag | | ^X.Y.Z | >= X.Y.Z and < (X+1).0.0 — compatible-with, allows minor and patch bumps | | ~X.Y.Z | >= X.Y.Z and < X.(Y+1).0 — patch bumps only | | X.Y.Z | exactly that version |
A tag counts as a release tag only when it is exactly three dot-separated runs of digits. latest, dev, 1.4, 1.10.1-rc and v1.4.2 are all ignored. There is no special handling of 0.x versions.
Implement:
def resolve_tag(tags, constraint):
Return the matching tag string with the highest version, or None when nothing matches.
tags = ["1.9.0", "1.10.0", "1.4.2", "2.0.0", "latest", "1.10.1-rc", "dev"]
print(resolve_tag(tags, "^1.4.0"))
Output:
1.10.0
2.0.0 is out of range for ^1.4.0; among 1.4.2, 1.9.0 and 1.10.0 the highest is 1.10.0 — which plain string sorting would have missed.
tags = ["1.9.0", "1.10.0", "1.4.2", "2.0.0", "latest", "1.10.1-rc", "dev"] print(resolve_tag(tags, "^1.4.0"))
1.10.0
Only 1.9.0, 1.10.0, 1.4.2 and 2.0.0 parse as release tags. ^1.4.0 admits [1.4.0, 2.0.0), dropping 2.0.0. Comparing the parsed tuples, (1, 10, 0) > (1, 9, 0), so 1.10.0 wins — string comparison would have picked 1.9.0.
1.10.0latest, dev, 1.4, 1.10.1-rc, v1.4.2) is ignoredconstraint is one of *, ^X.Y.Z, ~X.Y.Z or X.Y.Z0.x versionsNone when nothing matches