Add reader/writer for oci-archive multi image support#1381
Add reader/writer for oci-archive multi image support#1381umohnani8 wants to merge 1 commit intocontainers:mainfrom
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.
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.
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.
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.
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.
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.
|
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 |
|
Can you reply to https://github.com/containers/common/pull/921/files#r815770324? |
| } | ||
| succeeded = true | ||
| return &reader, nil | ||
| } |
|
|
||
| var d *imgspecv1.Descriptor | ||
|
|
||
| if ref.sourceIndex != -1 { |
There was a problem hiding this comment.
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.
Will do in a follow up PR
| d = &index.Manifests[ref.sourceIndex] | ||
| return *d, nil |
There was a problem hiding this comment.
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.
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.
(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.
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.
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 <qiwan@redhat.com>
Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
e6ece2b to
44465c9
Compare
|
Hi, any updates here?) |
mtrmac
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
(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.
(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.
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.
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.
(Merge problem) The use of t.TempDir() means callers don’t need to worry about cleanup.
| @@ -160,32 +178,5 @@ func (d *ociArchiveImageDestination) Commit(ctx context.Context, unparsedTopleve | |||
| 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.
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.
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 qiwan@redhat.com
Signed-off-by: Urvashi Mohnani umohnani@redhat.com