-
Notifications
You must be signed in to change notification settings - Fork 395
Add reader/writer for oci-archive multi image support #1381
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
|
@vrothberg @mtrmac PTAL |
|
Will look into it now. Note that it will require some massaging into |
vrothberg
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.
Before merging, we need some tests somewhere. For multi-image docker archives, we added them to Podman and those have since been moved to libimage (see https://github.com/containers/common/blob/main/libimage/save_test.go and https://github.com/containers/common/blob/main/libimage/load_test.go and https://github.com/containers/common/tree/main/libimage/testdata).
I suggest opening a PR against c/common vendoring in this PR. Once everything's green, we can merge here, then merge into libimage and then let it bubble up into Podman.
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.
WIP, so far I’ve just read parts of oci/archive.Reader.
Structurally it seems clean to me for oci/archive.ociArchiveImageSource to be always built on top of an oci/archive.Reader, and similarly for destination/Writer. I do feel somewhat strongly about this, OTOH the current approach around the existing tempDirOCIRef is probably less churn and easier to review, for now.
| } | ||
| succeeded = true | ||
| return &reader, 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.
docker/archive.Reader needs to provide a NewReaderForReference for libimage, AFAICS to turn an user-supplied docker-archive:…@0 into a tag used in c/storage on podman load.
Will something like that be necessary here? (Cc: @vrothberg )
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 update NewReader to take in a reference, if that is wrong I can break them apart tot two different functions NewReader and NewReaderForReference`.
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 think it would definitely make sense to provide a NewReader that accepts a path, so that direct usage of a Reader is simpler.
As to the NewReaderForReference, that requires looking at what c/common needs. The docker-archive variant requires NewReaderForReference also to return a reader-bound equivalent of the input reference (because we don’t otherwise expose ref.index and ref.sourceIndex, so the caller can’t do that itself), and Reader.ManifestTagsForReference. Looking at the nameFromAnnotations call in c/common, something vaguely like that might be necessary here.
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.
SGTM
|
As noted by @mtrmac, this PR should have a similar logic as in containers/common#853 (review) to account for archives generated with buildkit. |
03499a2 to
9e932c2
Compare
9e932c2 to
6b37365
Compare
6b37365 to
77ce5d8
Compare
|
@vrothberg @mtrmac I think I addressed most of the comments here, please let me know if I accidentally missed any. The c/common PR for this is containers/common#921 |
e1b0dbd to
e4964c0
Compare
1165cd5 to
460f04d
Compare
|
Can we please re-run the failed tests - failure looks like a flake. |
Retrigger ✔️ |
|
This is ready for review, c/common PR is green containers/common#921. Also test failure looks like a flake again |
| } | ||
| succeeded = true | ||
| return &reader, 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.
SGTM
|
|
||
| var d *imgspecv1.Descriptor | ||
|
|
||
| if ref.sourceIndex != -1 { |
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.
Non-blocking: WIth three cases, this could be a switch (and perhaps with an explicit “internal error” case if both image and sourceIndex are set).
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.
Will do in a follow up PR
| d = &index.Manifests[ref.sourceIndex] | ||
| return *d, 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.
Isn’t this just return index.Manifests[ref.sourceIndex]?
(Overall the d variable seems basically unnecessary — everything can just return a value, and the d == nil case only possible after the for loop`. OTOH cleaning up the pre-existing code is not really in scope, this PR is big enough.)
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.
Will do in a follow up PR
| return fmt.Errorf("Deleting images not implemented for oci: images") | ||
| } | ||
|
|
||
| // struct to store the ociReference and temporary directory returned by createOCIRef |
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.
(If LoadManifestDescriptor* is reimplemented) I imagine no users of tempDirOCIRef should remain — just Readers and Writers, now that they “own” their temporary directories. Maybe with a shared helper to create an oci/layout reference from a (temp dir, archive reference).
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.
will do in a follow up PR
460f04d to
c4be24d
Compare
63283ce to
6ce3216
Compare
2de595a to
e6ece2b
Compare
|
@mtrmac addressed all your comments PTAL |
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.
Thanks! Looks great overall.
The one real blocker is #1381 (comment) / containers/common#921 (comment) .
@umohnani8 could u has some update here? we look for this API canbe published, many thanks, |
Add reader/writer with helpers to allow podman save/load multi oci-archive images.
Allow read oci-archive using source_index to point to the index from oci-archive manifest.
Also reimplement ociArchiveImage{Source,Destination} to support this.
Signed-off-by: Qi Wang <[email protected]>
Signed-off-by: Urvashi Mohnani <[email protected]>
e6ece2b to
44465c9
Compare
|
Hi, any updates here?) |
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.
Good, there is now a NewReaderForReference — but it would be useful to have a at least a proof of concept in containers/common#921 before definitely committing to the API.
Let me just try to sketch what that would look like…
| if err := tempDirRef.deleteTempDir(); err != nil { | ||
| return nil, perrors.Wrapf(err, "deleting temp directory %q", tempDirRef.tempDirectory) | ||
| } | ||
| archive.Close() |
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 should only close individualWriterOrNil if it is set, not the longer-term ref.archiveWriter.
(in both cases.)
| // newImageDestination returns an ImageDestination for writing to an existing directory. | ||
| func newImageDestination(ctx context.Context, sys *types.SystemContext, ref ociArchiveReference) (private.ImageDestination, error) { | ||
| tempDirRef, err := createOCIRef(sys, ref.image) | ||
| func newImageDestination(ctx context.Context, sys *types.SystemContext, ref ociArchiveReference) (types.ImageDestination, 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.
I think this should continue to return private.ID, not types.ID. (We do, ultimately, have a test for the return value implementing private.ID, but having it explicit here would be a bit more convenient.)
| } else { | ||
| layoutRef, err = layout.NewReference(archive.tempDirectory, ref.image) | ||
| if err != nil { | ||
| archive.Close() |
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.
archive should only be closed if it is individualReaderOrNil. (in all cases.)
| func createOCIRef(sys *types.SystemContext, image string) (tempDirOCIRef, error) { | ||
| dir, err := os.MkdirTemp(tmpdir.TemporaryDirectoryForBigFiles(sys), "oci") | ||
| func createOCIRef(sys *types.SystemContext, image string, sourceIndex int) (tempDirOCIRef, error) { | ||
| dir, err := ioutil.TempDir(tmpdir.TemporaryDirectoryForBigFiles(sys), "oci") |
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.
(Merge problem) This should use the newer os.MkdirTemp name.
| // The caller should | ||
| // defer os.RemoveAll(tmpDir) | ||
| func refToTempOCI(t *testing.T, sourceIndex bool) (ref types.ImageReference, tmpDir string) { | ||
| tmpDir, err := ioutil.TempDir("", "oci-transport-test") | ||
| require.NoError(t, err) |
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.
(Merge problem) The use of t.TempDir makes the caller cleaning up unnecessary.
| if err := archive.NewDefaultArchiver().Untar(arch, dst, &archive.TarOptions{NoLchown: true}); err != nil { | ||
| return nil, perrors.Wrapf(err, "error untarring file %q", dst) | ||
| } | ||
|
|
||
| indexJSON, err := os.Open(filepath.Join(dst, "index.json")) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer indexJSON.Close() | ||
| reader.manifest = &imgspecv1.Index{} | ||
| if err := json.NewDecoder(indexJSON).Decode(reader.manifest); err != nil { | ||
| return nil, err | ||
| } |
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.
On these failures, the temporary directory should be removed.
| if refName != "" { | ||
| index = 1 | ||
| } | ||
| ref, err := newReference(r.path, refName, index, r, 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 looks rather incorrect:
- It is setting
indexexactly in the case where it is not needed (andnewReferencewould reject such an input) - The index needs to actually point at the right manifest, not a hard-coded
1.
| // The caller should | ||
| // defer os.RemoveAll(tmpDir) | ||
| func refToTempOCI(t *testing.T, sourceIndex bool) (ref types.ImageReference, tmpDir string) { | ||
| tmpDir, err := ioutil.TempDir("", "oci-transport-test") | ||
| require.NoError(t, err) |
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.
(Merge problem) The use of t.TempDir() means callers don’t need to worry about cleanup.
| // after the directory is made, it is tarred up into a file and the directory is deleted | ||
| func (d *ociArchiveImageDestination) Commit(ctx context.Context, unparsedToplevel types.UnparsedImage) error { | ||
| if err := d.unpackedDest.Commit(ctx, unparsedToplevel); err != nil { | ||
| return perrors.Wrapf(err, "storing image %q", d.ref.image) |
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.
pkg/errors are no longer used (throughout).
| archive = ref.archiveReader | ||
| individualReaderOrNil = nil | ||
| } else { | ||
| archive, _, err = NewReaderForReference(ctx, sys, ref) |
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 don’t see any benefit to calling NewReaderForReference if we are going to throw away that reference.
|
Prototyping this results in #1677 + containers/common#1178 . Note that the “pull” case still needs either |
|
Needs a rebase at this point. |
Add reader/writer with helpers to allow podman save/load multi oci-archive images.
Allow read oci-archive using source_index to point to the index from oci-archive manifest.
Also reimplement ociArchiveImage{Source,Destination} to support this.
Taking over #1072
Fixes containers/container-libs#257
Signed-off-by: Qi Wang [email protected]
Signed-off-by: Urvashi Mohnani [email protected]