github.com/azurearcforkubernetes/oras@v0.4.0/pkg/oras/pull.go (about)

     1  package oras
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	orascontent "github.com/deislabs/oras/pkg/content"
     8  
     9  	"github.com/containerd/containerd/content"
    10  	"github.com/containerd/containerd/images"
    11  	"github.com/containerd/containerd/log"
    12  	"github.com/containerd/containerd/remotes"
    13  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  // Pull pull files from the remote
    18  func Pull(ctx context.Context, resolver remotes.Resolver, ref string, ingester content.Ingester, opts ...PullOpt) (ocispec.Descriptor, []ocispec.Descriptor, error) {
    19  	if resolver == nil {
    20  		return ocispec.Descriptor{}, nil, ErrResolverUndefined
    21  	}
    22  	opt := pullOptsDefaults()
    23  	for _, o := range opts {
    24  		if err := o(opt); err != nil {
    25  			return ocispec.Descriptor{}, nil, err
    26  		}
    27  	}
    28  
    29  	_, desc, err := resolver.Resolve(ctx, ref)
    30  	if err != nil {
    31  		return ocispec.Descriptor{}, nil, err
    32  	}
    33  
    34  	fetcher, err := resolver.Fetcher(ctx, ref)
    35  	if err != nil {
    36  		return ocispec.Descriptor{}, nil, err
    37  	}
    38  
    39  	layers, err := fetchContent(ctx, fetcher, desc, ingester, opt)
    40  	if err != nil {
    41  		return ocispec.Descriptor{}, nil, err
    42  	}
    43  	return desc, layers, nil
    44  }
    45  
    46  func fetchContent(ctx context.Context, fetcher remotes.Fetcher, desc ocispec.Descriptor, ingester content.Ingester, opts *pullOpts) ([]ocispec.Descriptor, error) {
    47  	var descriptors []ocispec.Descriptor
    48  	lock := &sync.Mutex{}
    49  	picker := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
    50  		if isAllowedMediaType(desc.MediaType, opts.allowedMediaTypes...) {
    51  			if name, ok := orascontent.ResolveName(desc); ok && len(name) > 0 {
    52  				lock.Lock()
    53  				defer lock.Unlock()
    54  				descriptors = append(descriptors, desc)
    55  			}
    56  			return nil, nil
    57  		}
    58  		return nil, nil
    59  	})
    60  	store := newHybridStoreFromIngester(ingester)
    61  	handlers := []images.Handler{
    62  		filterHandler(opts.allowedMediaTypes...),
    63  	}
    64  	handlers = append(handlers, opts.baseHandlers...)
    65  	handlers = append(handlers,
    66  		remotes.FetchHandler(store, fetcher),
    67  		picker,
    68  		images.ChildrenHandler(store),
    69  	)
    70  	handlers = append(handlers, opts.callbackHandlers...)
    71  
    72  	if err := opts.dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	return descriptors, nil
    77  }
    78  
    79  func filterHandler(allowedMediaTypes ...string) images.HandlerFunc {
    80  	return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
    81  		switch {
    82  		case isAllowedMediaType(desc.MediaType, ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex):
    83  			return nil, nil
    84  		case isAllowedMediaType(desc.MediaType, allowedMediaTypes...):
    85  			if name, ok := orascontent.ResolveName(desc); ok && len(name) > 0 {
    86  				return nil, nil
    87  			}
    88  			log.G(ctx).Warnf("blob no name: %v", desc.Digest)
    89  		default:
    90  			log.G(ctx).Warnf("unknown type: %v", desc.MediaType)
    91  		}
    92  		return nil, images.ErrStopHandler
    93  	}
    94  }
    95  
    96  func isAllowedMediaType(mediaType string, allowedMediaTypes ...string) bool {
    97  	if len(allowedMediaTypes) == 0 {
    98  		return true
    99  	}
   100  	for _, allowedMediaType := range allowedMediaTypes {
   101  		if mediaType == allowedMediaType {
   102  			return true
   103  		}
   104  	}
   105  	return false
   106  }
   107  
   108  // dispatchBFS behaves the same as images.Dispatch() but in sequence with breath-first search.
   109  func dispatchBFS(ctx context.Context, handler images.Handler, descs ...ocispec.Descriptor) error {
   110  	for i := 0; i < len(descs); i++ {
   111  		desc := descs[i]
   112  		children, err := handler.Handle(ctx, desc)
   113  		if err != nil {
   114  			switch err := errors.Cause(err); err {
   115  			case images.ErrSkipDesc:
   116  				continue // don't traverse the children.
   117  			case ErrStopProcessing:
   118  				return nil
   119  			}
   120  			return err
   121  		}
   122  		descs = append(descs, children...)
   123  	}
   124  	return nil
   125  }