Skip to content

Improve support for manifest lists and image indexes#400

Merged
rhatdan merged 1 commit intocontainers:masterfrom
nalind:manifest-lists
Oct 21, 2019
Merged

Improve support for manifest lists and image indexes#400
rhatdan merged 1 commit intocontainers:masterfrom
nalind:manifest-lists

Conversation

@nalind
Copy link
Copy Markdown
Member

@nalind nalind commented Jan 13, 2018

This patch series adds a ManifestList interface, implemented by Schema2List and OCI1Index types, to the manifest package.
It then adds an instanceDigest parameter to the ImageSource.LayerInfosForCopy(), ImageDestination.PutManifest(), and ImageDestination.PutSignatures() methods, so that we can store multiple manifests to transports that can support them, and read them from sources which need to update the layer digest list.
It uses those parameters and updates copy.Image() to accept a MultipleImages boolean option which directs it to copy all instances if the source reference produces an ImageSource that represents multiple images.
It then finishes updating the containers-storage, directory, docker, and oci transports to be able to read and write multiple images at a time.

@nalind
Copy link
Copy Markdown
Member Author

nalind commented Jan 13, 2018

(There's a PutManifest() call in skopeo's cmd/skopeo/layers.go that we can't update here, so the make test-skopeo step will fail in Travis.)

Comment thread manifest/list.go
Comment thread copy/copy.go Outdated
@nalind nalind force-pushed the manifest-lists branch 3 times, most recently from dde2ae2 to e6a48b4 Compare January 16, 2018 15:45
Copy link
Copy Markdown
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

WIP, reviewed only the easy docker:/atomic:/dir: transport updates so far.

Comment thread types/types.go Outdated
Comment thread directory/directory_test.go Outdated
Comment thread directory/directory_test.go Outdated
Comment thread directory/directory_test.go Outdated
Comment thread docker/docker_image_dest.go
Comment thread docker/docker_image_dest.go Outdated
Comment thread docker/docker_image_src.go Outdated
Comment thread openshift/openshift.go Outdated
Comment thread openshift/openshift.go Outdated
Copy link
Copy Markdown
Collaborator

@mtrmac mtrmac left a comment

Choose a reason for hiding this comment

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

Makes sense overall.

Highlights are the determination of instance digests in destinations, and how OCI is supposed to work.

Comment thread manifest/manifest.go Outdated
Comment thread manifest/list.go Outdated
Comment thread manifest/list.go Outdated
Comment thread manifest/list.go Outdated
Comment thread manifest/list.go Outdated
Comment thread storage/storage_image.go Outdated
Comment thread storage/storage_test.go Outdated
Comment thread storage/storage_test.go Outdated
Comment thread storage/storage_image.go Outdated
Comment thread storage/storage_image.go Outdated
Comment thread storage/storage_image.go Outdated
Comment thread copy/copy.go Outdated
}
}
// ... but if we're forcing it, we prefer the forced value a bit more.
if forcedManifestListMIMEType != "" {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you do the for loop from 251-255 as part of an else for this if statement?

@nalind
Copy link
Copy Markdown
Member Author

nalind commented Feb 2, 2018

The linter's complaining about stuttering with the ManifestList and ManifestListUpdate types. Any preferences over renaming them to List and ListUpdate?

Comment thread storage/storage_image.go Outdated
// we just need to screen out the ones that are actually layers to get the list of non-layers.
if err = s.commitDataBlobs(img.ID); err != nil {
if _, err2 := s.imageRef.transport.store.DeleteImage(img.ID, true); err2 != nil {
logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On the fence here, but if you've an error deleting an incomplete image, should it just go to debug or should you wrap up err2 also and return with the return below?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ditto with the next few delete with a debug on error.

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.

copy.Image does that in a few (very few) cases, see if err := dest.Close(); err != nil {. All too often we do defer something.Close() and ignore the possible failure.

Either way it’s problematic. On one hand, yes, throwing away failure information may make diagnosing failures difficult. On the other, the copy.Image attempt to parenthesize the secondary failure has already confused at least one user, who focused on the secondary failure instead of the primary one.

Comment thread storage/storage_image.go Outdated
logrus.Debugf("error deleting incomplete image %q: %v", img.ID, err2)
}
logrus.Debugf("error saving manifest for image %q: %v", img.ID, err)
return err
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The errors prior to this you wrapped with a little verbiage. This one and below you did a debug then straight error. I'd vote continuing on with the error wrap using the veribage from the debugs, but your call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Changed them as you described.

Comment thread copy/copy.go Outdated
Comment thread copy/copy.go Outdated
Comment thread copy/copy.go Outdated
Comment thread manifest/list_test.go Outdated
Comment thread manifest/manifest.go Outdated
@mtrmac
Copy link
Copy Markdown
Collaborator

mtrmac commented Feb 2, 2018

The linter's complaining about stuttering with the ManifestList and ManifestListUpdate types. Any preferences over renaming them to List and ListUpdate?

manifest.List works for me. Definitely better than manifest.MultiArchImage :)

Comment thread copy/copy.go
Comment thread copy/copy.go Outdated
Comment thread copy/copy.go Outdated
Comment thread manifest/list.go Outdated
Comment thread manifest/list_test.go Outdated
Comment thread oci/layout/oci_dest_test.go Outdated
Comment thread storage/storage_image.go Outdated
Comment thread storage/storage_image.go Outdated
@rhatdan
Copy link
Copy Markdown
Member

rhatdan commented Jun 1, 2018

@nalind is this something you are still interested in or should we close this?

@mtrmac
Copy link
Copy Markdown
Collaborator

mtrmac commented Jun 1, 2018

@nalind is this something you are still interested in or should we close this?

The feature is definitely useful and eventually we would like to have it; this PR is about 90% there, so it seems more useful to keep it open and noticed once in a few months than to close it and have it forgotten forever.

owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.
owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
owtaylor added a commit to owtaylor/bodhi that referenced this pull request Jun 8, 2018
In order to support copying multi-arch containers and Flatpaks, we need
to be able to copy manifest lists and OCI image indexes from registry to
registry. Work is underway to add such support to skopeo
(containers/image#400), but as a temporary workaround
add 'bodhi-skopeo-lite', which implements the subset of 'skopeo copy' we need,
but with manifest list/image index support. Use of this needs to be specifically
configured.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
@bowlofeggs
Copy link
Copy Markdown

I would also really like OCI and manifest list support in Skopeo for https://github.com/fedora-infra/bodhi

@nalind
Copy link
Copy Markdown
Member Author

nalind commented Oct 3, 2019

Rebased.

@nalind nalind force-pushed the manifest-lists branch 2 times, most recently from f5eaa59 to 8f8d9c4 Compare October 7, 2019 13:30
@nalind
Copy link
Copy Markdown
Member Author

nalind commented Oct 7, 2019

Rebased.

@nalind
Copy link
Copy Markdown
Member Author

nalind commented Oct 14, 2019

Tweaked the description for CopyOnlyInstances.

@mrunalp
Copy link
Copy Markdown
Contributor

mrunalp commented Oct 15, 2019

@vrothberg ptal

Copy link
Copy Markdown
Member

@vrothberg vrothberg left a comment

Choose a reason for hiding this comment

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

First review. It's looking really good so far. I plan to get my hands dirty a bit and play with the code.

Comment thread copy/copy.go Outdated
Comment thread copy/copy.go Outdated
Comment thread copy/copy.go Outdated
Comment thread copy/copy.go
Comment thread copy/copy.go Outdated
Comment thread manifest/docker_list.go
Comment thread manifest/docker_list.go
Comment thread manifest/oci_index.go
Comment thread storage/storage_image.go
Comment thread storage/storage_reference.go
@nalind
Copy link
Copy Markdown
Member Author

nalind commented Oct 18, 2019

Updated.

Comment thread copy/copy.go Outdated
Comment thread copy/copy.go
// Copy each image, or just the ones we want to copy, in turn.
instanceDigests := list.Instances()
updates := make([]manifest.ListUpdate, len(instanceDigests))
for i, instanceDigest := range instanceDigests {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is still valid but I am cool to defer beautifying the copy reports to a future PR.

Copy link
Copy Markdown
Member

@vrothberg vrothberg left a comment

Choose a reason for hiding this comment

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

Just very minor nits. After that LGTM.

Comment thread copy/manifest.go Outdated
Comment thread docker/docker_image_dest.go Outdated
Add the manifest.List interface, and implementations for OCIv1 Index and
Docker Schema2List documents.

Add an instanceDigest parameter to PutManifest(), PutSignatures(), and
LayerInfosForCopy, for symmetry with GetManifest() and GetSignatures().
Return an error if the instanceDigest is supplied to destinations which
don't support them, and add stubs that do so even to the transports
which would support it, so that we don't break compilation here.

Add a MultipleImages flag to copy.Options, and if the source for a copy
operation contains multiple images, copy all of the images if we can.
If we can't copy them all, but we were told to, return an error.

Use the generic manifest list API to select a single image to copy from
a list, so that we aren't just limited to the Docker manifest list
format for those cases.

When guessing at the type of a manifest, if the manifest contains a list
of manifests, use its declared MIME type if it included one, else assume
it's an OCI index, because an OCI index doesn't include its MIME type.

When copying, switch from using an encode-then-compare of the original
and updated versions of the list to checking if the instance list was
changed (one of the things we might have changed) or if its type has
changed due to conversion (the other change we might have made).  If
neither has changed, then we don't need to change the encoded value of
the manifest.

When copying, when checking for a digest mismatch in a target image
reference, ignore a mismatch between the digest in the reference and the
digest of the main manifest if we're copying one element from a list,
and the digest in the reference matches the digest of the manifest list.

When copying, if conversion of manifests for single images is being
forced, convert manifest lists to the corresponding list types.

When copying, supply the unparsed top level to Commit() by attaching the
value to the context.Context.

Support manifest lists in the directory transport by using the instance
digest as a prefix of the filename used to store a manifest or a piece
of signature data.

Support manifest lists in the oci-layout transport by accepting indexes
as we do images, and stop guessing about Platform values to add to the
top-level index.

Support storing manifest lists to registries in the docker: transport by
using the manifest digest when we're writing one image as part of
pushing a list of them, and by using the instance digest when reading or
writing signature data, when one is specified, or the cached digest of
the non-instanced digest when one is not specified.

Add partial support for manifest lists to the storage transport: when
committing one image from a list into storage, also add a copy of the
manifest list by extracting it from the context.Context.  The logic is
already in place to enable locating an image using any of multiple
manifest digests.

When writing an image that has an instanceDigest value (meaning it's a
secondary image), don't try to generate a canonical reference to add to
the image's list of names if the reference for the primary image doesn't
contain a name.  That should only happen if we're writing using just an
image ID, which is unlikely, but we still need to handle it.

Avoid computing the digest of the manifest, or retrieving the
either-a-tag-or-a-digest value from the target reference, if we're given
an instanceDigest, which would override them anyway.

Move the check for non-nil instanceDigest values up into the main
PutSignatures() method instead of duplicating it in the per-strategy
helpers.

Add mention of the instanceDigest parameter and its use to various
PutManifest, PutSignatures, and LayerInfosForCopy implementations and
their declarations in interfaces.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
@nalind
Copy link
Copy Markdown
Member Author

nalind commented Oct 21, 2019

Dropped the patch that redirected to containers/skopeo#741 for testing, so I expect the test-skopeo target to fail.

Copy link
Copy Markdown
Member

@vrothberg vrothberg left a comment

Choose a reason for hiding this comment

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

LGTM

@rhatdan
Copy link
Copy Markdown
Member

rhatdan commented Oct 21, 2019

LGTM
As expected without the DONOTMERGE patch, tests fail.
Merging.

@grooverdan
Copy link
Copy Markdown
Contributor

Thank you all for writing and reviewing this feature. 🍰

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.