oras
oras copied to clipboard
bug: discrepancy between reference parsing using oras
What happened in your environment?
Hey Peeps! :)
We found an interesting conundrum when dealing with oci layers and outlines on disk with --oci-layout.
It looks like, the parsing of the Ref on the cli for oras doesn't follow how a tag could look like? A tag accepts an absolute reference value but here it needs to be relative.
What am I talking about?
I'm talking about this function: https://github.com/oras-project/oras/blob/6f04cf05b29fa8eb7dd0cc0b636d45fa716911d1/cmd/oras/internal/option/target.go#L133
And then this: https://github.com/oras-project/oras/blob/main/cmd/oras/internal/fileref/unix.go#L25
And here is the tag ref grammar: https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
The reproducing steps:
oras cp --to-oci-layout ghcr.io/stefanprodan/podinfo:6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0
oras manifest fetch --oci-layout ./:6.8.0
# edit the `index.json` such that the annotation for index is the full ref: annotations: ["org.opencontainers.image.ref.name":"ghcr.io/stefanprodan/podinfo:6.8.0"]
oras manifest fetch --oci-layout ./:ghcr.io/stefanprodan/podinfo:6.8.0 # this now errors even though technically, it should be correct
➜ oras manifest fetch --oci-layout ./:ghcr.io/stefanprodan/podinfo:6.8.0
Error: invalid argument "./:ghcr.io/stefanprodan/podinfo:6.8.0": failed to find path "./:ghcr.io/stefanprodan/podinfo": stat ./:ghcr.io/stefanprodan/podinfo: no such file or directory
throws errors because it's biting off the end of the absolute path and then looks for that directory.
It's worth mentioning that the library does work. So there is some weirdness somewhere. :)
If this is indeed a discrepancy, we are fully committed to provide a fix as well. So we are going to do the work. :)
Thanks :bow:
What did you expect to happen?
Parsing of the reference to be working with relative and absolute path as well.
How can we reproduce it?
oras cp --to-oci-layout ghcr.io/stefanprodan/podinfo:6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0
oras manifest fetch --oci-layout ./:6.8.0
# edit the `index.json` such that the annotation for index is the full ref: annotations: ["org.opencontainers.image.ref.name":"ghcr.io/stefanprodan/podinfo:6.8.0"]
oras manifest fetch --oci-layout ./:ghcr.io/stefanprodan/podinfo:6.8.0 # this now errors even though technically, it should be correct
➜ oras manifest fetch --oci-layout ./:ghcr.io/stefanprodan/podinfo:6.8.0
Error: invalid argument "./:ghcr.io/stefanprodan/podinfo:6.8.0": failed to find path "./:ghcr.io/stefanprodan/podinfo": stat ./:ghcr.io/stefanprodan/podinfo: no such file or directory
What is the version of your ORAS CLI?
➜ oras version Version: 1.2.2+Homebrew Go version: go1.23.4
What is your OS environment?
osx, linux
Are you willing to submit PRs to fix it?
- [x] Yes, I am willing to fix it.
Why not?
oras manifest fetch --oci-layout ./ghcr.io/stefanprodan/podinfo:6.8.0
oras manifest fetch --oci-layout ./ghcr.io/stefanprodan/podinfo:6.8.0
Error: invalid argument "./ghcr.io/stefanprodan/podinfo:6.8.0": failed to find path "./ghcr.io/stefanprodan/podinfo": stat ./ghcr.io/stefanprodan/podinfo: no such file or directory
I was confused on why you had : in the path and your instructions would not put that in the path.
After I modify the index.json, I get
Error: failed to fetch the content of "./ghcr.io/stefanprodan/podinfo:6.8.0": not found
but not the "failed to find path" that you got. The error message I got is not very helpful, because it is probably something like a mismatch on expected reference.
What is the reason to modify the index.json? Both references should be there:
{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df","size":2385,"annotations":{"org.opencontainers.image.ref.name":"6.8.0"}},
{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df","size":2385,"annotations":{"org.opencontainers.image.ref.name":"ghcr.io/stefanprodan/podinfo:6.8.0"}},
What do you mean by "should be there"? Do you mean that oras fetch should have put in both...? Because right now, that's all the index.json file is:
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df",
"size": 2385,
"annotations": {
"org.opencontainers.image.ref.name": "6.8.0"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:365b1952433cbb4cf0e8bea4bb9b243096f6f987f7e8b32ae0e3f5a480a33650",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:4468906882c4ff094af439835d5a76719cb6feea98bb8ea087fc8964f3568610",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47190298cb07dbb845dd29445b827dbe4a1cb4d92157182343e40c8b60319ffb",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"size": 1627,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"size": 1627,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"size": 1625,
"platform": {
"architecture": "arm64",
"os": "linux"
}
}
]
}
I can reproduce the error. It is caused by oras-cli only treating the string after the last : as tag. It does not consider cases in which a tag contains :. But this may be tricky to fix, as directory path is also allowed to have :.
Nice, thanks very much.
After running oras cp --to-oci-layout ghcr.io/stefanprodan/podinfo:6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0, I got the following directory structure:
$ tree .
.
└── ghcr.io
└── stefanprodan
└── podinfo
├── blobs
│ └── sha256
│ ├── 0654843d1f45e2e689e29a7c05c5affade2084a76bf03e63ada6c5f33d32d590
│ ├── 076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996
│ ├── 0aae13986da760b285bdd0e26d1152d9207bd0ecf29b3747e9f9dd7b67042513
│ ├── 0c28a55df19d49ef125e83bb7d40ea3360e6f0b916e35bc5ea7a0ec431afd359
│ ├── 214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1
│ ├── 2b0fcbb1f62fc2623534a1d970004f3fc9e1080271e6fb165bc5e88fa35a1469
│ ├── 2bf3ba7b8ebd88461be2d8dc346e0e3fbfef2d71ea40955c828adf612958195d
│ ├── 3600fdc6126f9d836b5428dea7bed7ef2cc61ccd84f62069040937f32edcd7bb
│ ├── 365b1952433cbb4cf0e8bea4bb9b243096f6f987f7e8b32ae0e3f5a480a33650
│ ├── 3f9c6681619de458b6ec5659835a6dd1f2ebcbc9ba88320f1484331d7518e893
│ ├── 4468906882c4ff094af439835d5a76719cb6feea98bb8ea087fc8964f3568610
│ ├── 47190298cb07dbb845dd29445b827dbe4a1cb4d92157182343e40c8b60319ffb
│ ├── 47ca6f5331bad2a49bad341fecbd93ba330f00c1f86b5f5d8f5a512a45e8a9e4
│ ├── 4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1
│ ├── 581143f6696816ad3afdfb7ebf44d584b38d07f0ae0a700921d8eebdfa3892a3
│ ├── 6089034c246b857670b84d82ba3c807ea1b5d9cafe2569c99d1c70bd9c94c278
│ ├── 6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df
│ ├── 6e771e15690e2fabf2332d3a3b744495411d6e0b00b2aea64419b58b0066cf81
│ ├── 79fd453b97f7a47f77272088d76d74c36e4bdc04c44586bfe9cc393521c2da84
│ ├── 7c5533c09b9f28a5d1c2a8d7cda288d456d9ac33b037de17863f6fb664220163
│ ├── 85f3b18f9f5a8655db86c6dfb02bb01011ffef63d10a173843c5c65c3e9137b7
│ ├── 897f04f58f3cbe605f37df9562c3d3a9a864fae29f09268e66511c82de898a2a
│ ├── 8e30b3ee087cc5a1b7a083cbe5fee74670ad75583ace86edcd110abbe8ea3cec
│ ├── 9a3025d851d81ca41aa912a2cfadce13442e4f64a9c36724798783dee0170bfc
│ ├── 9b6b71e6a17b544e522350b2a912c65a020906a220d6ba4dcd7e752e7bae6165
│ ├── a44ca8345dfa31ae76d31de00f88a441a37962adbcbc6e163bae500a7545052b
│ ├── a917e6f592d4bb4e2073676b0ff92ec4c378656581fd8bfeb967f6b7db5fe17d
│ ├── bb9c56543049d2edcadd9fc238a3112aa7247c982a9b0902e15c1bf83e7a4fc8
│ ├── c11d0af14aad5a76c08e62982645c21b17b24353a54dfe23d4cd0037b03854e6
│ ├── d5f961ac2905042f3b88d80870b5cbe9c953c1ace58a09b869e7f7ac89e7bf3c
│ ├── d8a81c3b621a13e6aa942c4b8fc5b33e43ee7ac03782ca0634b931355431d40f
│ ├── dbe87e0a233a21e1a1fc7c24e940a34c6197816785af15a9b946f961bf655094
│ ├── dd1a152f3ff0cdf76983df685418f4eed2986fc43771d04c2189b52763d600c6
│ ├── e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13
│ ├── e4d09708fd762b84825c47fac2eaf352758e6252a605c9ac70cab6fa2860badd
│ ├── f18232174bc91741fdf3da96d85011092101a032a93a388b79e99e69c2d5c870
│ └── f9eb9faa9ed816b9a2744c7d6d97db3f638e3cad4d0a527ddbcd208b931518a1
├── index.json
├── ingest
└── oci-layout
In this structure, the root directory of the OCI layout is ghcr.io/stefanprodan/podinfo, with ghcr.io and stefanprodan as parent directories.
With this, I'm able to get the manifest content by running:
oras manifest fetch --oci-layout ghcr.io/stefanprodan/podinfo:6.8.0
or
oras manifest fetch 6.8.0 --oci-layout-path ghcr.io/stefanprodan/podinfo
Differences between the two commands:
- The first command treats the string after
:as the tag and everything before:as the path to the OCI layout root. - The second command treats
6.8.0as the target tag and the parameter of--oci-layout-pathas the OCI layout root.
To check the index.json, I run:
cat ghcr.io/stefanprodan/podinfo/index.json | jq .
The output was:
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df",
"size": 2385,
"annotations": {
"org.opencontainers.image.ref.name": "6.8.0"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:4468906882c4ff094af439835d5a76719cb6feea98bb8ea087fc8964f3568610",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47190298cb07dbb845dd29445b827dbe4a1cb4d92157182343e40c8b60319ffb",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"size": 1625,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"size": 1627,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"size": 1627,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:365b1952433cbb4cf0e8bea4bb9b243096f6f987f7e8b32ae0e3f5a480a33650",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}
To use the full reference ghcr.io/stefanprodan/podinfo:6.8.0 as a tag, I run:
oras tag 6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0 --oci-layout-path ghcr.io/stefanprodan/podinfo
The index.json will then become:
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df",
"size": 2385,
"annotations": {
"org.opencontainers.image.ref.name": "ghcr.io/stefanprodan/podinfo:6.8.0"
}
},
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:6c1975b871efb327528c84d46d38e6dd7906eecee6402bc270eeb7f1b1a506df",
"size": 2385,
"annotations": {
"org.opencontainers.image.ref.name": "6.8.0"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"size": 1627,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:365b1952433cbb4cf0e8bea4bb9b243096f6f987f7e8b32ae0e3f5a480a33650",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:4468906882c4ff094af439835d5a76719cb6feea98bb8ea087fc8964f3568610",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:076337b0f03bf33430505b37898390a6090efea255222a647d10a05ed2926996",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:47190298cb07dbb845dd29445b827dbe4a1cb4d92157182343e40c8b60319ffb",
"size": 840,
"annotations": {
"vnd.docker.reference.digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:e17bf402cd8ff41a322d12dc013678bf03e3558909c5a3a32388cbe157292f13",
"size": 1625,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:214be3e0fcce3be28e948e838eea34d6e3d9b24ce0689d7b879477a5d4b709d1",
"size": 1627,
"platform": {
"architecture": "amd64",
"os": "linux"
}
}
]
}
This allows me to get the same manifest by running:
oras manifest fetch ghcr.io/stefanprodan/podinfo:6.8.0 --oci-layout-path ghcr.io/stefanprodan/podinfo
@Skarlso Does the same flow work for you? 🤔
Problem is that we are unsure if we are even using things appropriately. That folder structure is actually not OCI compliant, right? It's just something ORAS did. The library store interface only works with a root in the filesystem. But can't handle multiple roots.
https://github.com/oras-project/oras-go/blob/main/content/oci/readonlyoci.go and https://github.com/oras-project/oras-go/blob/main/content/file/file.go do not support this behavior unless we misunderstand how we are supposed to use it together with this file structure.
If you have a better example or a guide in how to do that, we are also open to that. Maybe we are doing something wrong. :)
For the command:
oras cp --to-oci-layout ghcr.io/stefanprodan/podinfo:6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0
The target ghcr.io/stefanprodan/podinfo:6.8.0 is interpreted with everything before : as the path and everything after : as the target tag. This is why a directory structure is created. Essentially, ghcr.io/stefanprodan/podinfo is just a path to the root podinfo and doesn't have any special meaning—it's similar to foo/bar/podinfo or ./podinfo.
To avoid this, you can use a simpler path for the OCI layout folder:
oras cp ghcr.io/stefanprodan/podinfo:6.8.0 podinfo:6.8.0 --to-oci-layout
Here, the target OCI root is podinfo and the target tag is 6.8.0.
Alternatively, you can use:
oras cp ghcr.io/stefanprodan/podinfo:6.8.0 ghcr.io/stefanprodan/podinfo:6.8.0 --oci-layout-path podinfo
In this case, the target OCI root is podinfo and the target tag is the full reference ghcr.io/stefanprodan/podinfo:6.8.0.
Did this answer your question? Are you using CLI or SDK? 🤔
Basically the same issue: #1505
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.
Ah great. :D I think that fixes our use case as well. I'm so sorry for being absent on this bug. 🙇 Not very polite from me. :/
This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 30 days.
This issue was closed because it has been stalled for 30 days with no activity.