Skip to content

feat: implement oci conversion#1293

Closed
thesayyn wants to merge 7 commits intogoogle:mainfrom
thesayyn:oci-conversion
Closed

feat: implement oci conversion#1293
thesayyn wants to merge 7 commits intogoogle:mainfrom
thesayyn:oci-conversion

Conversation

@thesayyn
Copy link
Copy Markdown
Contributor

@thesayyn thesayyn commented Feb 16, 2022

Implements #1245. cc @jonjohnsonjr

Open to any kind of suggestions. I did my best to keep the patch as small as possible.

I will add tests after i address the changes.

the patch in action: thesayyn/oci-image:latest-crane pulled debian and pushed via crane.

@imjasonh
Copy link
Copy Markdown
Contributor

Sorry for the delay in responding to this. Even more apologies if these are topics you've already discussed. 😞

+1 to changing the OCI annotations to match the recommended/reserved standard.

I'm slightly -1 to having a "just mutate this manifest" method, since it breaks some invariants we have in place otherwise, and we might find out those invariants are load-bearing. I believe it should be possible to achieve the same result just using mutate.AppendLayers(empty.Image, layers), mutate.ConfigFile, and mutate.Annotations. Is there something else you specifically need from mutate.ImageManifest?

I agree that having --format and --convert is a bit confusing. I wonder if we might just want a single-purpose crane ocify command that takes a docker-typed image||index and pushes over it with the OCI-flavored version. This could be used in a bash pipeline to achieve the same thing, without the confusing flags.

I'm not totally convinced even this is a good idea, so if we go down this path I might want us to make it a hidden CLI surface, with docs saying that it might go away in the future.

In fact, as I'm thinking of it, would a command that does just that be sufficient for your use case, even outside of crane? I don't believe there's anything happening in this PR that can't be done using existing methods outside of this repo (correct me if I'm wrong), so it should be possible to unblock you just using code you write yourself. I'm sensitive to me becoming a speedbump for you, if you have a need for this sooner than I can review.

Let me know what you think.

@thesayyn
Copy link
Copy Markdown
Contributor Author

thesayyn commented Feb 24, 2022

I'm slightly -1 to having a "just mutate this manifest" method, since it breaks some invariants we have in place otherwise, and we might find out those invariants are load-bearing. I believe it should be possible to achieve the same result just using mutate.AppendLayers(empty.Image, layers), mutate.ConfigFile, and mutate.Annotations. Is there something else you specifically need from mutate.ImageManifest?

I am on the same page with you. I used mutators for everything else except for changing the manifest. the reason i did that is I wanted to preserve the ordering of the layer field. in theory i could have just do removeLayers and appendLayers but i am only interested in some specific layers so I could, in theory, displace the order of a layer and break the image. I didn't realize there was a mutate.ImageManifest.

I wonder if we might just want a single-purpose crane ocify command that takes a docker-typed image||index and pushes over it with the OCI-flavored version.

having --format oci and ocify would be even more confusing IMHO :D. maybe we rename the option to be something more descriptive? i believe options should have been --layout=oci|tar --format=oci|auto .

I'm not totally convinced even this is a good idea, so if we go down this path I might want us to make it a hidden CLI surface, with docs saying that it might go away in the future.
I'd like to know more about concerns maybe I am doing something wrong? I didn't think it would be a bad idea to have this given that a few tools already do this and is a well-defined standard and supported by almost every container runtime/registry.

In fact, as I'm thinking of it, would a command that does just that be sufficient for your use case, even outside of crane?

I feel your pain as a maintainer that you don't feel like landing a feature unless there is a great public interest. I do that myself too. a lot of people really don't care about what mediaTypes their container images have, hell they don't know what it is as long as their deployment works. However, I am under the impression this would useful for others as well and I want to land it here.

my use case was to rip out any randomness from the base images. I'll try to enumerate my points

1- docker specification has randomness built-in into their images. it is so easy to break reproducibility and docker helps that with properties such as docker_version container. they are just random and bound to their build environment. an example; building the same image with the same mtime etc yields a different hash.
2- properties such as health check and their friends are not portable and yet still docker is encouraging it.
3- this is the standard that docker should be following but they just do whatever they want as an industry dominator.

also given the size of the change, it wouldn't be such a big maintenance burden.

I'm sensitive to me becoming a speedbump for you, if you have a need for this sooner than I can review.

I don't want you to land anything here if you don't feel strongly about it. I am just trying to push crane to a place where you can build images on the disk. (amend RFC).

Yet still, if you think my arguments are not good enough, then maybe we could hide this option behind an experimental flag so that people depend on it carefully and beware that it might go away if it is good enough?

@thesayyn
Copy link
Copy Markdown
Contributor Author

you could be anticipating some unforeseen issues with this as well. but if there is anything i can address, i am happy to address them.

@thesayyn
Copy link
Copy Markdown
Contributor Author

thesayyn commented Jun 5, 2022

I have addressed the issues above. I'd be happy if someone could take another look. I'd really like reach to a conclusion about whether to accept this or not

@imjasonh
Copy link
Copy Markdown
Contributor

imjasonh commented Jun 6, 2022

Hey @thesayyn, sorry I let this linger for so long. I'm definitely still interested in this functionality, there's been a need for it in ko before, and even some attempts to make it work over there. The main blocker has been ensuring that the OCI-ified image is considered valid for a variety of OCI registries. Unfortunately, some have different behaviors around mixing OCI and Docker media types, and there isn't a lot of consistency, so our best bet is just lots of manual testing 😭

I'll chat with @jonjohnsonjr about it this week and see if we can make some progress. Thanks for hanging on this long, and thanks for this contribution.

@thesayyn
Copy link
Copy Markdown
Contributor Author

thesayyn commented Jul 1, 2022

Mixing docker media types definitely should not be prohibited. Maybe we could avoid this case when the image contains layers that don't have OCI counterparts?

Or even better, we could have an option to whether hard fail when an image can't be converted to be 100% OCI.

@hown3d
Copy link
Copy Markdown

hown3d commented Jul 5, 2022

This is something that's also interesting (almost neccessary) for ko-build/ko#738.

To provide annotations for the wasi image, OCI format is neccessary and for example quay.io prohibits mixed layers.

@thesayyn
Copy link
Copy Markdown
Contributor Author

thesayyn commented Aug 10, 2022

I'll chat with @jonjohnsonjr about it this week and see if we can make some progress. Thanks for hanging on this long, and thanks for this contribution.

I started working on rules_oci. we kinda need this. This seems to affect other people as well. See project-zot/zot#622

So far the concerns were;

  • mixing mediaTypes
  • no counterpart for DockerPluginConfig and DockerForeignLayer
  • losing cosign signatures due to mismatching signatures

@thesayyn
Copy link
Copy Markdown
Contributor Author

thesayyn commented Aug 10, 2022

@imjasonh just had another pass at this. removing changes already landed. skimmed down to just oci conversion.

FYI. I am getting strange EOF errors when I replace tarball.LayerFromReader with tarball.LayerFromOpener

@github-actions
Copy link
Copy Markdown

github-actions bot commented Nov 9, 2022

This Pull Request is stale because it has been open for 90 days with
no activity. It will automatically close after 30 more days of
inactivity. Keep fresh with the 'lifecycle/frozen' label.

Comment on lines +86 to +106
case types.DockerLayer:
reader, err := layer.Compressed()
if err != nil {
return nil, fmt.Errorf("getting layer: %w", err)
}
layer, err = tarball.LayerFromOpener(func() (io.ReadCloser, error) { return reader, nil }, tarball.WithMediaType(types.OCILayer))
if err != nil {
return nil, fmt.Errorf("building layer: %w", err)
}
case types.DockerUncompressedLayer:
reader, err := layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("getting layer: %w", err)
}
layer, err = tarball.LayerFromOpener(func() (io.ReadCloser, error) { return reader, nil }, tarball.WithMediaType(types.OCIUncompressedLayer))
if err != nil {
return nil, fmt.Errorf("building layer: %w", err)
}
}
newLayers = append(newLayers, layer)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I suspect there is a better way to do this, this is rather expensive.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'd expect that but i wasn't able to find it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ping @jonjohnsonjr do you have suggestions to make this less expensive? This PR seems stuck.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is annoying because of how partial.Descriptor works, but I would probaby create a new type, something like:

type layerWithMediaType struct {
  v1.Layer
  mt types.MediaType
}

func (l *layerWithMediaType) Descriptor() (*v1.Descriptor, error) {
  // call partial.Descriptor on wrapped layer and update MediaType
}

func (l *layerWithMediaType) MediaType() (types.MediaType, error) {
  return l.mt, nil
}

@github-actions
Copy link
Copy Markdown

github-actions bot commented May 8, 2023

This Pull Request is stale because it has been open for 90 days with
no activity. It will automatically close after 30 more days of
inactivity. Keep fresh with the 'lifecycle/frozen' label.

@alexeagle
Copy link
Copy Markdown

Not stale

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants