1
15 package oras
16
17 import (
18 "bytes"
19 "context"
20 "fmt"
21 "sync"
22
23 "github.com/containerd/containerd/content"
24 "github.com/containerd/containerd/errdefs"
25 "github.com/containerd/containerd/images"
26 "github.com/containerd/containerd/log"
27 "github.com/containerd/containerd/remotes"
28 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
29 "oras.land/oras-go/pkg/target"
30 )
31
32
33
34
35 func Copy(ctx context.Context, from target.Target, fromRef string, to target.Target, toRef string, opts ...CopyOpt) (ocispec.Descriptor, error) {
36 if from == nil {
37 return ocispec.Descriptor{}, ErrFromTargetUndefined
38 }
39 if to == nil {
40 return ocispec.Descriptor{}, ErrToTargetUndefined
41 }
42
43 if toRef == "" {
44 toRef = fromRef
45 }
46 opt := copyOptsDefaults()
47 for _, o := range opts {
48 if err := o(opt); err != nil {
49 return ocispec.Descriptor{}, err
50 }
51 }
52
53 if from == nil {
54 return ocispec.Descriptor{}, ErrFromResolverUndefined
55 }
56 if to == nil {
57 return ocispec.Descriptor{}, ErrToResolverUndefined
58 }
59
60
61
62
63 _, desc, err := from.Resolve(ctx, fromRef)
64 if err != nil {
65 return ocispec.Descriptor{}, err
66 }
67
68 fetcher, err := from.Fetcher(ctx, fromRef)
69 if err != nil {
70 return ocispec.Descriptor{}, err
71 }
72
73 pushRef := fmt.Sprintf("%s@%s", toRef, desc.Digest.String())
74 pusher, err := to.Pusher(ctx, pushRef)
75 if err != nil {
76 return ocispec.Descriptor{}, err
77 }
78
79 if err := transferContent(ctx, desc, fetcher, pusher, opt); err != nil {
80 return ocispec.Descriptor{}, err
81 }
82 return desc, nil
83 }
84
85 func transferContent(ctx context.Context, desc ocispec.Descriptor, fetcher remotes.Fetcher, pusher remotes.Pusher, opts *copyOpts) error {
86 var descriptors, manifests []ocispec.Descriptor
87 lock := &sync.Mutex{}
88 picker := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
89 if isAllowedMediaType(desc.MediaType, opts.allowedMediaTypes...) {
90 if opts.filterName(desc) {
91 lock.Lock()
92 defer lock.Unlock()
93 descriptors = append(descriptors, desc)
94 }
95 return nil, nil
96 }
97 return nil, nil
98 })
99
100
101
102
103 store := opts.contentProvideIngesterPusherFetcher
104 if store == nil {
105 store = newHybridStoreFromPusher(pusher, opts.cachedMediaTypes, true)
106 }
107
108
109 baseFetchHandler := func(p remotes.Pusher, f remotes.Fetcher) images.HandlerFunc {
110 return images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
111 cw, err := p.Push(ctx, desc)
112 if err != nil {
113 if !errdefs.IsAlreadyExists(err) {
114 return nil, err
115 }
116
117 return nil, nil
118 }
119 defer cw.Close()
120
121 rc, err := f.Fetch(ctx, desc)
122 if err != nil {
123 return nil, err
124 }
125 defer rc.Close()
126 return nil, content.Copy(ctx, cw, rc, desc.Size, desc.Digest)
127 })
128 }
129
130
131 fetchHandler := images.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
132 if isAllowedMediaType(desc.MediaType, opts.cachedMediaTypes...) {
133 lock.Lock()
134 defer lock.Unlock()
135 manifests = append(manifests, desc)
136 }
137 return baseFetchHandler(store, fetcher)(ctx, desc)
138 })
139
140 handlers := []images.Handler{
141 filterHandler(opts, opts.allowedMediaTypes...),
142 }
143 handlers = append(handlers, opts.baseHandlers...)
144 handlers = append(handlers,
145 fetchHandler,
146 picker,
147 images.ChildrenHandler(&ProviderWrapper{Fetcher: store}),
148 )
149 handlers = append(handlers, opts.callbackHandlers...)
150
151 if err := opts.dispatch(ctx, images.Handlers(handlers...), nil, desc); err != nil {
152 return err
153 }
154
155
156
157 for i := len(manifests) - 1; i >= 0; i-- {
158 _, err := baseFetchHandler(pusher, store)(ctx, manifests[i])
159 if err != nil {
160 return err
161 }
162 }
163
164
165 if opts.saveManifest != nil && len(manifests) > 0 {
166 rc, err := store.Fetch(ctx, manifests[0])
167 if err != nil {
168 return fmt.Errorf("could not get root manifest to save based on CopyOpt: %v", err)
169 }
170 defer rc.Close()
171 buf := new(bytes.Buffer)
172 if _, err := buf.ReadFrom(rc); err != nil {
173 return fmt.Errorf("unable to read data for root manifest to save based on CopyOpt: %v", err)
174 }
175
176 opts.saveManifest(buf.Bytes())
177 }
178
179
180 if opts.saveLayers != nil && len(descriptors) > 0 {
181 opts.saveLayers(descriptors)
182 }
183 return nil
184 }
185
186 func filterHandler(opts *copyOpts, allowedMediaTypes ...string) images.HandlerFunc {
187 return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
188 switch {
189 case isAllowedMediaType(desc.MediaType, ocispec.MediaTypeImageManifest, ocispec.MediaTypeImageIndex):
190 return nil, nil
191 case isAllowedMediaType(desc.MediaType, allowedMediaTypes...):
192 if opts.filterName(desc) {
193 return nil, nil
194 }
195 log.G(ctx).Warnf("blob no name: %v", desc.Digest)
196 default:
197 log.G(ctx).Warnf("unknown type: %v", desc.MediaType)
198 }
199 return nil, images.ErrStopHandler
200 }
201 }
202
203 func isAllowedMediaType(mediaType string, allowedMediaTypes ...string) bool {
204 if len(allowedMediaTypes) == 0 {
205 return true
206 }
207 for _, allowedMediaType := range allowedMediaTypes {
208 if mediaType == allowedMediaType {
209 return true
210 }
211 }
212 return false
213 }
214
View as plain text