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 }