-
Notifications
You must be signed in to change notification settings - Fork 395
Add client support for opencontainers/distribution-spec #819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Add a new transport with URL prefix called "dist://" This is an implementation of an OCI image repository client, based on the opencontainers/distribution-spec. For a full commit history, please see https://github.com/anuvu/image/commits/master Consequently, a container image tool like skopeo when linked with this version of containers/image can do: "skopeo copy dist://src-server dist://dest-server" where src-server and dest-server are both OCI dist-spec compliant servers. For the current state of server-side implementations of opencontainers/distribution-spec, please see https://oci.bloodorange.io/ Signed-off-by: Ramkumar Chinchani <[email protected]> Signed-off-by: Serge Hallyn <[email protected]> Signed-off-by: Tycho Andersen <[email protected]>
|
Additional discussion about this topic at: |
|
It might be nice to call this "oci-dist" or something; it's not really clear what "dist" means. |
|
Thanks for the PR. I’ll make a quick pass through the code and add a few notes in a while. Most importantly, though, are there actually any real differences between
From a maintenance point of view, it is rather burdensome to have two significantly different implementations of substantially the same functionality, including one of the two transports that are most important and most often updated, for little end-user benefit. I don’t doubt that eventually there will be a fairly significant divergence, and I’m sure the
If it makes sense to introduce the new transport name and syntax right now, at all (which is unclear, especially WRT bothering users with the transition, given ~zero end-user benefit, but I’m open to considering that), I’m very insistent that it should share 99% of the code with the existing But my first instinct is that the current |
mtrmac
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most importantly, please see the other comment about the undesirability of maintaining substantially the same functionality twice.
There is also not a single line of tests; how would this code keep working? (The docker:// transport is tested mostly via skopeo integration tests — definitely not ideal, but it’s something.)
| } | ||
|
|
||
| //nolint (funlen) | ||
| func NewOciRepo(ref *distReference, sys *types.SystemContext) (r OciRepo, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this have to be public at all? The c/image transports primarily exist to provide an unified interface, and we have a lot of trouble keeping the API stable enough, so I’m very hesitant about introducing new symbols without good justification.
| port := "8080" | ||
| hostName := "" | ||
|
|
||
| if ref.server != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, please. A localhost registry seems like a pretty rare case to me, not really worth introducing a syntax variation (and especially not worth worrying about whether the user uses localhost or 127.0.0.1 for certificate names). String parsing of references is hard enough in the best cases.
Just have the user specify the server explicitly.
| if sys != nil { | ||
| if sys.DockerAuthConfig != nil { | ||
| a := sys.DockerAuthConfig | ||
| creds = base64.StdEncoding.EncodeToString([]byte(a.Username + ":" + a.Password)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why hard-code the implementation details here, when the code could store the user name and password separately, and have req.SetBasicAuth deal with the HTTP specifics?
| creds = base64.StdEncoding.EncodeToString([]byte(a.Username + ":" + a.Password)) | ||
| } else { | ||
| registry := fmt.Sprintf("%s:%s", server, port) | ||
| if username, password, err := config.GetAuthentication(sys, registry); err == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not how config typically refers to registries: if the user does not specify a port, the registry parameter should not explicitly include one. If this code does not handle input the same way, things like podman login won’t create entries that will work.
| //nolint (funlen) | ||
| func NewOciRepo(ref *distReference, sys *types.SystemContext) (r OciRepo, err error) { | ||
| server := "127.0.0.1" | ||
| port := "8080" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the OCI spec say anything about the default port? Won’t changing the default require users to explicitly add ports to interact with the vast majority of currently used public registries? And, see below, that would mean that the credential items set by podman login wouldn’t be found.
| } | ||
|
|
||
| func (ref distReference) DockerReference() reference.Named { | ||
| return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? The references, AFAIK, use exactly the same namespace, and are pretty much 100% interoperable, and the DockerReference value is important for signing and for naming images in local storage (e.g. so that podman pull tags the result with the location of the image on the registry).
| port := "" | ||
|
|
||
| if ref.port != -1 { | ||
| port = fmt.Sprintf("%d/", ref.port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look right; .port == -1 will result in "//server:fullname".
| } | ||
|
|
||
| func (ref distReference) PolicyConfigurationIdentity() string { | ||
| return ref.StringWithinTransport() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PolicyConfigurationIdentity is supposed to be canonical, i.e. adjust for the defaulted server/port values.
| github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= | ||
| github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= | ||
| github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= | ||
| github.com/containers/image v3.0.2+incompatible h1:B1lqAE8MUPCrsBLE86J0gnXleeRq8zJnQryhiiGQNyE= |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please drop this, to make sure the non-v5 imports never get in.
| type distTransport struct{} | ||
|
|
||
| func (s distTransport) Name() string { | ||
| return "dist" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we will need bike-shedding over this :)
|
For reference, some previous conversation about the |
|
If the two distribution mechanisms get merged, it would be nice if the "docker" was changed to "dist" so we could remove a company name from a distribution type. |
|
Maybe one way to think about this: what’s the end-user use case / mechanism? So far, it seems to be possible to support Docker schema1+schema2 + OCI with a single syntax, single workflow, single transport, single set of mostly consistent features, mostly automatically doing the right thing (there are definitely a few cases where the code doesn’t detect limited-capability registries, even if it should be technically possible, and users have to explicitly choose a manifest format — but none of them are fundamentally impossible to fix, AFAIK). If there are two different transports, how do users interact with this? Are they completely interchangeable? If users use CLIs that deemphasize the transport: syntax (like Starting with a protocol implementation without being clear on how the software is going to be used seems backwards to me. Users, ultimately, care very little about specifications, specification names, and fairly little about conformance of any particular implementation to any particular standard — working UIs that don’t get in the way so often trump all that. |
|
I am quite excited about conforming with the OCI dist spec. Thanks a lot for tackling that! I generally agree with @mtrmac's assessment. Certainly, using In this PR, we should wire in the OCI support into |
|
@mtrmac and others, thanks for the detailed comments on the PR. As per suggestions, I am working on another PR to incorporate OCI support into docker:// transport code itself and let it evolve from there. |
|
After some more investigation, it appears that perhaps no additional changes are needed in containers/image itself. https://github.com/containers/skopeo/blob/master/docs/skopeo-copy.1.md works just fine. The support for "format" option appears to have been added a while back. |
|
So it seems the goal should be to get thinks working in skopeo automatically, i.e. autodetecting the --format argument? |
|
c/image is autodetecting[1] whether the destination supports an image format, and a conversion happens if the destination rejects it. In base case, in which the registry supports the source image format without modification, that image is copied without conversion unless the user explicitly asks otherwise. (That way signatures, if any, and manifest digests are preserved if possible.) What behavior are you looking for? Always converting an image to OCI even if the registry supports storing the original format does not seem to be obviously and unambiguously the right thing. [1] There are a few known deficiencies, IIRC e.g. one registry implementation that accepts OCI without error but can’t return it: that one could be detected but currently isn’t. But those are implementation deficiencies, not deliberate design decisions. |
For starters, if the source is OCI and the destination speaks OCI, it seems reasonable to keep it as OCI. But I'm not sure I generally agree with this statement. Why not use the standard instead of a custom format where available? |
Isn’t that already happening? If it isn’t, that’s most likely a bug.
Because every conversion has some small risk of breaking the image itself, and a much bigger risk of changing how the image is treated by its consumers. (And there are other small reasons like schema2, but not OCI, supporting health check commands — or maybe that is just the Podman implementation, not sure.) |
Possibly, I don't know. I do know that you can't write to zot without --format=oci, so some part of the detection is broken. |
|
(Also, the distinction between ”standard” and ”a custom format” is much less clear-cut that that wording implies, given how much software out there (still) doesn’t support OCI in full/correctly. Sure, that’s a chicken-and-egg problem, but forcing the issue by defaulting to OCI if the support is not seamless is not that likely to help — it’s more likely to motivate users to sprinkle scripts with |
|
@rchincha @vrothberg @mtrmac @tych0 Any update on this? |
@rhatdan: couple of things going on:
References: |
|
I’m going to close this, at least for now. If at all possible the existing c/image/docker code should be able to automatically work with both OCI and old docker/distribution registries, without user involvement. I’m fairly sure it’s not a 100% compliant client right now (IIRC in the authentication behavior, if nothing else); it may turn out that the goal of a single automagic implementation is not achievable, and in that case we may need to revisit this. |
Add a new transport with URL prefix called "dist://"
This is an implementation of an OCI image repository client, based on the
opencontainers/distribution-spec.
For a full commit history, please see https://github.com/anuvu/image/commits/master
Consequently, a container image tool like skopeo when linked with this
version of containers/image can do:
"skopeo copy dist://src-server dist://dest-server"
where src-server and dest-server are both OCI dist-spec compliant
servers.
For the current state of server-side implementations of opencontainers/distribution-spec,
please see https://oci.bloodorange.io/
Signed-off-by: Ramkumar Chinchani [email protected]
Signed-off-by: Serge Hallyn [email protected]
Signed-off-by: Tycho Andersen [email protected]