Merge pull request #8744 from cardyok/bugfix_remote_fetch_mediatype

This commit is contained in:
Fu Wei 2023-07-04 21:58:35 +08:00 committed by GitHub
commit e7276fe35a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 8 deletions

View File

@ -451,7 +451,12 @@ var (
Usage: "Retrieve blobs from a remote", Usage: "Retrieve blobs from a remote",
ArgsUsage: "[flags] <remote> [<digest>, ...]", ArgsUsage: "[flags] <remote> [<digest>, ...]",
Description: `Fetch blobs by digests from a remote.`, Description: `Fetch blobs by digests from a remote.`,
Flags: commands.RegistryFlags, Flags: append(commands.RegistryFlags, []cli.Flag{
cli.StringFlag{
Name: "media-type",
Usage: "Specify target mediatype for request header",
},
}...),
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
var ( var (
ref = context.Args().First() ref = context.Args().First()
@ -486,7 +491,7 @@ var (
if err != nil { if err != nil {
return err return err
} }
rc, _, err := fetcherByDigest.FetchByDigest(ctx, dgst) rc, _, err := fetcherByDigest.FetchByDigest(ctx, dgst, remotes.WithMediaType(context.String("media-type")))
if err != nil { if err != nil {
return err return err
} }

View File

@ -29,6 +29,7 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/remotes"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -151,12 +152,18 @@ func (r dockerFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.R
}) })
} }
func (r dockerFetcher) createGetReq(ctx context.Context, host RegistryHost, ps ...string) (*request, int64, error) { func (r dockerFetcher) createGetReq(ctx context.Context, host RegistryHost, mediatype string, ps ...string) (*request, int64, error) {
headReq := r.request(host, http.MethodHead, ps...) headReq := r.request(host, http.MethodHead, ps...)
if err := headReq.addNamespace(r.refspec.Hostname()); err != nil { if err := headReq.addNamespace(r.refspec.Hostname()); err != nil {
return nil, 0, err return nil, 0, err
} }
if mediatype == "" {
headReq.header.Set("Accept", "*/*")
} else {
headReq.header.Set("Accept", strings.Join([]string{mediatype, `*/*`}, ", "))
}
headResp, err := headReq.doWithRetries(ctx, nil) headResp, err := headReq.doWithRetries(ctx, nil)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -175,9 +182,15 @@ func (r dockerFetcher) createGetReq(ctx context.Context, host RegistryHost, ps .
return getReq, headResp.ContentLength, nil return getReq, headResp.ContentLength, nil
} }
func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest) (io.ReadCloser, ocispec.Descriptor, error) { func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest, opts ...remotes.FetchByDigestOpts) (io.ReadCloser, ocispec.Descriptor, error) {
var desc ocispec.Descriptor var desc ocispec.Descriptor
ctx = log.WithLogger(ctx, log.G(ctx).WithField("digest", dgst)) ctx = log.WithLogger(ctx, log.G(ctx).WithField("digest", dgst))
var config remotes.FetchByDigestConfig
for _, o := range opts {
if err := o(ctx, &config); err != nil {
return nil, desc, err
}
}
hosts := r.filterHosts(HostCapabilityPull) hosts := r.filterHosts(HostCapabilityPull)
if len(hosts) == 0 { if len(hosts) == 0 {
@ -196,7 +209,7 @@ func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest) (i
) )
for _, host := range r.hosts { for _, host := range r.hosts {
getReq, sz, err = r.createGetReq(ctx, host, "blobs", dgst.String()) getReq, sz, err = r.createGetReq(ctx, host, config.Mediatype, "blobs", dgst.String())
if err == nil { if err == nil {
break break
} }
@ -209,7 +222,7 @@ func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest) (i
if getReq == nil { if getReq == nil {
// Fall back to the "manifests" endpoint // Fall back to the "manifests" endpoint
for _, host := range r.hosts { for _, host := range r.hosts {
getReq, sz, err = r.createGetReq(ctx, host, "manifests", dgst.String()) getReq, sz, err = r.createGetReq(ctx, host, config.Mediatype, "manifests", dgst.String())
if err == nil { if err == nil {
break break
} }
@ -231,7 +244,7 @@ func (r dockerFetcher) FetchByDigest(ctx context.Context, dgst digest.Digest) (i
} }
seeker, err := newHTTPReadSeeker(sz, func(offset int64) (io.ReadCloser, error) { seeker, err := newHTTPReadSeeker(sz, func(offset int64) (io.ReadCloser, error) {
return r.open(ctx, getReq, "", offset) return r.open(ctx, getReq, config.Mediatype, offset)
}) })
if err != nil { if err != nil {
return nil, desc, err return nil, desc, err

View File

@ -65,7 +65,7 @@ type FetcherByDigest interface {
// FetcherByDigest usually returns an incomplete descriptor. // FetcherByDigest usually returns an incomplete descriptor.
// Typically, the media type is always set to "application/octet-stream", // Typically, the media type is always set to "application/octet-stream",
// and the annotations are unset. // and the annotations are unset.
FetchByDigest(ctx context.Context, dgst digest.Digest) (io.ReadCloser, ocispec.Descriptor, error) FetchByDigest(ctx context.Context, dgst digest.Digest, opts ...FetchByDigestOpts) (io.ReadCloser, ocispec.Descriptor, error)
} }
// Pusher pushes content // Pusher pushes content
@ -92,3 +92,20 @@ type PusherFunc func(ctx context.Context, desc ocispec.Descriptor) (content.Writ
func (fn PusherFunc) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) { func (fn PusherFunc) Push(ctx context.Context, desc ocispec.Descriptor) (content.Writer, error) {
return fn(ctx, desc) return fn(ctx, desc)
} }
// FetchByDigestConfig provides configuration for fetching content by digest
type FetchByDigestConfig struct {
//Mediatype specifies mediatype header to append for fetch request
Mediatype string
}
// FetchByDigestOpts allows callers to set options for fetch object
type FetchByDigestOpts func(context.Context, *FetchByDigestConfig) error
// WithMediaType sets the media type header for fetch request
func WithMediaType(mediatype string) FetchByDigestOpts {
return func(ctx context.Context, cfg *FetchByDigestConfig) error {
cfg.Mediatype = mediatype
return nil
}
}