Skip to content

#10681 by-digest ctr image export of org.opencontainers.image.ref.name #13167

Open
lauralorenz wants to merge 2 commits intocontainerd:mainfrom
lauralorenz:10681-ctr-image-export-oci-ref-name
Open

#10681 by-digest ctr image export of org.opencontainers.image.ref.name #13167
lauralorenz wants to merge 2 commits intocontainerd:mainfrom
lauralorenz:10681-ctr-image-export-oci-ref-name

Conversation

@lauralorenz
Copy link
Copy Markdown

@lauralorenz lauralorenz commented Apr 6, 2026

Fixes #10681

Adds an integration test and fixes the issue where ctr image exports for images by digest had a non-valid grammar set in their org.opencontainers.image.ref.name field in their index.json.

The way I chose to handle this is so that if an export is called for something that has only a sha256 digest, it will return the full name (as requested in the issue), but that in all other cases it stays the same (which in general would mean that this would refer to the image's tag, or the tag@sha256:<hash>. (see examples below)

Alternatively, I could change it to always prefer the full name in all of those cases, which is implied to be requested in the issue, if we don't like this. However, if we want to discuss that please check the Implementer's Note at the index.json spec in OCI image-spec, which to me makes it seem like it may be beneficial to leave this as the tag as often as possible.

PS: I spent some time writing tests against import since the ociAnnotationRef function is used there, but its only in a specific case (docker image layouts) and it seems to not work (or at least not be very testable with the test harness for import we have).

Current behavior / bad

# digest only (the bad one!)
laura@k8s-dev-station:~/go/src/containerd$ ctr image export - docker.io/library/python@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66 | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66",
    "org.opencontainers.image.ref.name": "@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66"
  }
]

# tag only
laura@k8s-dev-station:~/go/src/containerd$ ctr image export -  docker.io/library/python:latest | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python:latest",
    "org.opencontainers.image.ref.name": "latest"
  }
]
# tag and digest
laura@k8s-dev-station:~/go/src/containerd$ ctr image export - docker.io/library/python:3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python:3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b",
    "org.opencontainers.image.ref.name": "3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b"
  }
]

Behavior in this PR / good

# digest only (different now!)
laura@k8s-dev-station:~/go/src/containerd$ ctr image export - docker.io/library/python@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66 | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66",
    "org.opencontainers.image.ref.name": "docker.io/library/python@sha256:4651409ff55024245f70920259ecee27f2109b0dc0e502a10bce541f1072cd66"
  }
]

# tag only (same)
laura@k8s-dev-station:~/go/src/containerd$ ctr image export -  docker.io/library/python:latest | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python:latest",
    "org.opencontainers.image.ref.name": "latest"
  }
]

# tag and digest (same)
laura@k8s-dev-station:~/go/src/containerd$ ctr image export - docker.io/library/python:3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b | tar -x --to-stdout index.json | jq "[.manifests[0].annotations]"
[
  {
    "containerd.io/distribution.source.docker.io": "library/python",
    "io.containerd.image.name": "docker.io/library/python:3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b",
    "org.opencontainers.image.ref.name": "3.13.13-slim@sha256:f96eb0214ceab47efc2558b8351888ca01acf6193f4050ee7594c8250516cc8b"
  }
]

@github-project-automation github-project-automation Bot moved this to Needs Triage in Pull Request Review Apr 6, 2026
Copilot AI review requested due to automatic review settings April 9, 2026 20:37
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from 63edfc9 to bf6bd02 Compare April 9, 2026 20:37
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch 4 times, most recently from d6fad45 to 8e1847e Compare April 10, 2026 02:31
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from 8e1847e to dcc9d60 Compare April 10, 2026 02:33
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from dcc9d60 to 867e97e Compare April 10, 2026 03:38
Uses the definition of valid grammar for this field
from the OCI image annotations spec:
https://github.com/opencontainers/image-spec/blob/e72ae99d5fc74e7f7f8e320a44f76968da86a545/annotations.md#pre-defined-annotation-keys

On this commit the test will fail per the bug containerd#10681
`manifest annotation org.opencontainers.image.ref.name
="@sha256:7b3ccabffc97de872a30dfd234fd972a66d247c8cfc69b0550f276481852627c"
 does not match required grammar`

Signed-off-by: Laura Lorenz <lauralorenz@google.com>
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch 2 times, most recently from ee79e05 to fa1529a Compare April 10, 2026 18:55
@lauralorenz lauralorenz marked this pull request as ready for review April 10, 2026 19:54
@lauralorenz lauralorenz changed the title [WIP] by-digest ctr image export of org.opencontainers.image.ref.name #10681 by-digest ctr image export of org.opencontainers.image.ref.name Apr 10, 2026

func ociReferenceName(name string) string {
// OCI defines the reference name as only a tag excluding the
// OCI suggests the reference name as only a tag excluding the
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dosubot dosubot Bot added area/distribution Image Distribution kind/bug labels Apr 10, 2026
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from da73208 to 4614b67 Compare April 13, 2026 20:02
@lauralorenz
Copy link
Copy Markdown
Author

/rerun

@lauralorenz
Copy link
Copy Markdown
Author

/retest

@k8s-ci-robot
Copy link
Copy Markdown

@lauralorenz: Cannot trigger testing until a trusted user reviews the PR and leaves an /ok-to-test message.

Details

In response to this:

/retest

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@samuelkarp
Copy link
Copy Markdown
Member

/ok-to-test

@lauralorenz
Copy link
Copy Markdown
Author

/retest

@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from 4614b67 to aa630ff Compare April 13, 2026 21:54
@samuelkarp
Copy link
Copy Markdown
Member

/retest

@samuelkarp
Copy link
Copy Markdown
Member

samuelkarp commented Apr 14, 2026

It looks like a couple of the commits are in the style of "address reviews". We generally prefer to have commits that tell a story of the change. Preserving the changes from review (and I expect these were driven by the LLM review I sent you? 😅) isn't particularly valuable. Can you incorporate the fixes into the commits where they were relevant?

Edit: Having looked through the commits, I think this could either be all squashed into one commit or have one commit for the test and another for the fix. Either would be fine, but we don't need more than two commits here. Once the commits are fixed this LGTM.

@lauralorenz
Copy link
Copy Markdown
Author

Roger that, squashing 🫡 and will keep that in mind next time!

@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from aa630ff to cc3f640 Compare April 14, 2026 23:00
@samuelkarp samuelkarp requested a review from chrishenzie April 14, 2026 23:27
Copy link
Copy Markdown
Member

@tianon tianon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, I could change it to always prefer the full name in all of those cases, which is implied to be requested in the issue, if we don't like this.

I'm obviously biased (author of the issue and I apply a patch in my own builds of containerd that does exactly that 😂).

To be clear: I am not a containerd maintainer (so take all my review with the according [non]weight) 🙇

}
}

func assertOCIIndexAnnotationRefName(t *testing.T, r io.Reader) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is standard in the containerd repo, but it seems weird IMO to have a single-use helper function 🤔 why not inline this? are the other tests doing this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel too strongly either way, originally it was simply to match style in the other tests but in those cases the helper functions are reused. I do think it's a bit easier to read the test case itself this way since the helper function does a bunch of uninteresting file parsing.

Comment thread integration/client/helpers.go Outdated
"regexp"
)

var OCIAnnotationRegex = regexp.MustCompile(`^[A-Za-z0-9]+(?:(?:[-._:@+]|--)[A-Za-z0-9]+)*(?:/[A-Za-z0-9]+(?:(?:[-._:@+]|--)[A-Za-z0-9]+)*)*$`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely eyebrow-raising, IMO - this looks like it's probably that ref.name grammar, expressed as a regex?

I don't know what the containerd project norm is here, but I'd suggest the name be more specific to that (because ref.name is the only OCI annotation that matches this grammar), and maybe even link to the grammar? It also feels a little strange for it to live in a completely separate file, not right next to (or even inside) the single function that actually uses it, and definitely doesn't need it exported (this is a module nobody is likely to import, but it's good hygiene regardless).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X-post with #13167 (comment) that I have at least renamed/unexported this variable.

Regarding code structure: I initially put it in an existing helper file (as it felt like a helper/util to me) but realized later I needed it to be platform agnostic at those helper files are built per platform. I don't feel strongly about it being broken out though, and have folded it into the related assertion now, so please take another look.

Comment thread integration/client/client_unix_test.go Outdated
Comment thread integration/client/helpers_unix_test.go Outdated

const newLine = "\n"

var OCIAnnotationRegex = regexp.MustCompile(`^[A-Za-z0-9]+(?:(?:[-._:@+]|--)[A-Za-z0-9]+)*(?:/[A-Za-z0-9]+(?:(?:[-._:@+]|--)[A-Za-z0-9]+)*)*$`)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @tianon's comment, I think we should probably clarify the naming of this var (since there's many different annotation keys) and document where this regex is derived from, maybe https://github.com/opencontainers/image-spec/blob/v1.1.1/annotations.md#pre-defined-annotation-keys ?

Copy link
Copy Markdown
Author

@lauralorenz lauralorenz Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

X-post with #13167 (comment)

I've renamed this variable and added in a comment with the reference

@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from cc3f640 to 7652bf6 Compare April 15, 2026 22:07
On export, if the image is by-digest without any tag,
set the org.opencontainers.image.ref.name as the full name.
This prevents setting this field with a leading non-alphanum,
which is incorrect OCI grammar. Fixes containerd#10681.

Signed-off-by: Laura Lorenz <lauralorenz@google.com>
@lauralorenz lauralorenz force-pushed the 10681-ctr-image-export-oci-ref-name branch from 7652bf6 to 75d32fd Compare April 15, 2026 22:16
@lauralorenz
Copy link
Copy Markdown
Author

/retest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Needs Triage

Development

Successfully merging this pull request may close these issues.

ctr image export with a by-digest reference creates an invalid org.opencontainers.image.ref.name annotation value

5 participants