-
Notifications
You must be signed in to change notification settings - Fork 380
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
Add reader/writer for oci-archive multi image support #1381
Conversation
@vrothberg @mtrmac PTAL |
Will look into it now. Note that it will require some massaging into |
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.
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 |
Can you reply to https://github.com/containers/common/pull/921/files#r815770324? |
} | ||
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
oci/archive/oci_transport.go
Outdated
@@ -144,7 +178,7 @@ func (ref ociArchiveReference) NewImageDestination(ctx context.Context, sys *typ | |||
|
|||
// DeleteImage deletes the named image from the registry, if supported. | |||
func (ref ociArchiveReference) DeleteImage(ctx context.Context, sys *types.SystemContext) error { | |||
return errors.Errorf("Deleting images not implemented for oci: images") | |||
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 Reader
s and Writer
s, 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 |
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?) |
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
index
exactly in the case where it is not needed (andnewReference
would 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.
@@ -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.
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 #1116
Signed-off-by: Qi Wang [email protected]
Signed-off-by: Urvashi Mohnani [email protected]