Merge pull request #9029 from dmcgowan/push-inherit-distribution-sources

push: inherit distribution sources from parent
This commit is contained in:
Maksym Pavlenko 2023-09-07 12:46:18 -07:00 committed by GitHub
commit c13f47a3ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 21 deletions

View File

@ -68,8 +68,13 @@ Most of this is experimental and there are few leaps to make this work.`,
Usage: "Pull content from all platforms", Usage: "Pull content from all platforms",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "all-metadata", Name: "all-metadata",
Usage: "Pull metadata for all platforms", Usage: "(Deprecated: use skip-metadata) Pull metadata for all platforms",
Hidden: true,
},
cli.BoolFlag{
Name: "skip-metadata",
Usage: "Skips metadata for unused platforms (Image may be unable to be pushed without metadata)",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "metadata-only", Name: "metadata-only",
@ -141,7 +146,7 @@ func NewFetchConfig(ctx context.Context, clicontext *cli.Context) (*FetchConfig,
config.AllMetadata = true config.AllMetadata = true
// Any with an empty set is None // Any with an empty set is None
config.PlatformMatcher = platforms.Any() config.PlatformMatcher = platforms.Any()
} else if clicontext.Bool("all-metadata") { } else if !clicontext.Bool("skip-metadata") {
config.AllMetadata = true config.AllMetadata = true
} }

View File

@ -63,8 +63,13 @@ command. As part of this process, we do the following:
Usage: "Pull content and metadata from all platforms", Usage: "Pull content and metadata from all platforms",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "all-metadata", Name: "all-metadata",
Usage: "Pull metadata for all platforms", Usage: "(Deprecated: use skip-metadata) Pull metadata for all platforms",
Hidden: true,
},
cli.BoolFlag{
Name: "skip-metadata",
Usage: "Skips metadata for unused platforms (Image may be unable to be pushed without metadata)",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "print-chainid", Name: "print-chainid",
@ -123,7 +128,7 @@ command. As part of this process, we do the following:
// Any with an empty set is None // Any with an empty set is None
// TODO: Specify way to specify not default platform // TODO: Specify way to specify not default platform
// config.PlatformMatcher = platforms.Any() // config.PlatformMatcher = platforms.Any()
} else if context.Bool("all-metadata") { } else if !context.Bool("skip-metadata") {
sopts = append(sopts, image.WithAllMetadata) sopts = append(sopts, image.WithAllMetadata)
} }

View File

@ -87,9 +87,6 @@ type IngestManager interface {
} }
// Info holds content specific information // Info holds content specific information
//
// TODO(stevvooe): Consider a very different name for this struct. Info is way
// to general. It also reads very weird in certain context, like pluralization.
type Info struct { type Info struct {
Digest digest.Digest Digest digest.Digest
Size int64 Size int64
@ -111,12 +108,17 @@ type Status struct {
// WalkFunc defines the callback for a blob walk. // WalkFunc defines the callback for a blob walk.
type WalkFunc func(Info) error type WalkFunc func(Info) error
// Manager provides methods for inspecting, listing and removing content. // InfoProvider provides info for content inspection.
type Manager interface { type InfoProvider interface {
// Info will return metadata about content available in the content store. // Info will return metadata about content available in the content store.
// //
// If the content is not present, ErrNotFound will be returned. // If the content is not present, ErrNotFound will be returned.
Info(ctx context.Context, dgst digest.Digest) (Info, error) Info(ctx context.Context, dgst digest.Digest) (Info, error)
}
// Manager provides methods for inspecting, listing and removing content.
type Manager interface {
InfoProvider
// Update updates mutable information related to content. // Update updates mutable information related to content.
// If one or more fieldpaths are provided, only those // If one or more fieldpaths are provided, only those

View File

@ -27,7 +27,6 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -61,8 +60,7 @@ var LineTreeFormat = TreeFormat{
type ContentReader interface { type ContentReader interface {
content.Provider content.Provider
content.InfoProvider
Info(ctx context.Context, dgst digest.Digest) (content.Info, error)
} }
type ImageTreePrinter struct { type ImageTreePrinter struct {

View File

@ -204,8 +204,9 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc
// Base handlers can be provided which will be called before any push specific // Base handlers can be provided which will be called before any push specific
// handlers. // handlers.
// //
// If the passed in content.Provider is also a content.Manager then this will // If the passed in content.Provider is also a content.InfoProvider (such as
// also annotate the distribution sources in the manager. // content.Manager) then this will also annotate the distribution sources using
// labels prefixed with "containerd.io/distribution.source".
func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, store content.Provider, limiter *semaphore.Weighted, platform platforms.MatchComparer, wrapper func(h images.Handler) images.Handler) error { func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, store content.Provider, limiter *semaphore.Weighted, platform platforms.MatchComparer, wrapper func(h images.Handler) images.Handler) error {
var m sync.Mutex var m sync.Mutex
@ -234,7 +235,7 @@ func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, st
platformFilterhandler := images.FilterPlatforms(images.ChildrenHandler(store), platform) platformFilterhandler := images.FilterPlatforms(images.ChildrenHandler(store), platform)
var handler images.Handler var handler images.Handler
if m, ok := store.(content.Manager); ok { if m, ok := store.(content.InfoProvider); ok {
annotateHandler := annotateDistributionSourceHandler(platformFilterhandler, m) annotateHandler := annotateDistributionSourceHandler(platformFilterhandler, m)
handler = images.Handlers(annotateHandler, filterHandler, pushHandler) handler = images.Handlers(annotateHandler, filterHandler, pushHandler)
} else { } else {
@ -340,14 +341,15 @@ func FilterManifestByPlatformHandler(f images.HandlerFunc, m platforms.Matcher)
// annotateDistributionSourceHandler add distribution source label into // annotateDistributionSourceHandler add distribution source label into
// annotation of config or blob descriptor. // annotation of config or blob descriptor.
func annotateDistributionSourceHandler(f images.HandlerFunc, manager content.Manager) images.HandlerFunc { func annotateDistributionSourceHandler(f images.HandlerFunc, provider content.InfoProvider) images.HandlerFunc {
return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
children, err := f(ctx, desc) children, err := f(ctx, desc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// only add distribution source for the config or blob data descriptor // Distribution source is only used for config or blob but may be inherited from
// a manifest or manifest list
switch desc.MediaType { switch desc.MediaType {
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest, case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest,
images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
@ -355,12 +357,28 @@ func annotateDistributionSourceHandler(f images.HandlerFunc, manager content.Man
return children, nil return children, nil
} }
// parentInfo can be used to inherit info for non-existent blobs
var parentInfo *content.Info
for i := range children { for i := range children {
child := children[i] child := children[i]
info, err := manager.Info(ctx, child.Digest) info, err := provider.Info(ctx, child.Digest)
if err != nil { if err != nil {
return nil, err if !errdefs.IsNotFound(err) {
return nil, err
}
if parentInfo == nil {
pi, err := provider.Info(ctx, desc.Digest)
if err != nil {
return nil, err
}
parentInfo = &pi
}
// Blob may not exist locally, annotate with parent labels for cross repo
// mount or fetch. Parent sources may apply to all children since most
// registries enforce that children exist before the manifests.
info = *parentInfo
} }
for k, v := range info.Labels { for k, v := range info.Labels {