Skip to content

Commit 7963626

Browse files
authored
feat: introduce CopyError to identify error source in copy operations (#933)
1. Introduce a new error type `CopyError` and return it from `Copy` and `ExtendedCopy` when necessary 2. Add corresponding unit tests 3. Add a example for the `CopyError` type Resolve #677 Signed-off-by: Lixia (Sylvia) Lei <[email protected]>
1 parent 6dd6913 commit 7963626

10 files changed

+870
-53
lines changed

copy.go

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ type CopyGraphOptions struct {
131131
// Returns the descriptor of the root node on successful copy.
132132
func Copy(ctx context.Context, src ReadOnlyTarget, srcRef string, dst Target, dstRef string, opts CopyOptions) (ocispec.Descriptor, error) {
133133
if src == nil {
134-
return ocispec.Descriptor{}, errors.New("nil source target")
134+
return ocispec.Descriptor{}, newCopyError("Copy", CopyErrorOriginSource, errors.New("nil source target"))
135135
}
136136
if dst == nil {
137-
return ocispec.Descriptor{}, errors.New("nil destination target")
137+
return ocispec.Descriptor{}, newCopyError("Copy", CopyErrorOriginDestination, errors.New("nil destination target"))
138138
}
139139
if dstRef == "" {
140140
dstRef = srcRef
@@ -147,14 +147,14 @@ func Copy(ctx context.Context, src ReadOnlyTarget, srcRef string, dst Target, ds
147147
proxy := cas.NewProxyWithLimit(src, cas.NewMemory(), opts.MaxMetadataBytes)
148148
root, err := resolveRoot(ctx, src, srcRef, proxy)
149149
if err != nil {
150-
return ocispec.Descriptor{}, fmt.Errorf("failed to resolve %s: %w", srcRef, err)
150+
return ocispec.Descriptor{}, err
151151
}
152152

153153
if opts.MapRoot != nil {
154154
proxy.StopCaching = true
155155
root, err = opts.MapRoot(ctx, proxy, root)
156156
if err != nil {
157-
return ocispec.Descriptor{}, err
157+
return ocispec.Descriptor{}, newCopyError("MapRoot", CopyErrorOriginSource, err)
158158
}
159159
proxy.StopCaching = false
160160
}
@@ -174,6 +174,12 @@ func Copy(ctx context.Context, src ReadOnlyTarget, srcRef string, dst Target, ds
174174
// from the source CAS to the destination CAS.
175175
// The root node (e.g. a manifest of the artifact) is identified by a descriptor.
176176
func CopyGraph(ctx context.Context, src content.ReadOnlyStorage, dst content.Storage, root ocispec.Descriptor, opts CopyGraphOptions) error {
177+
if src == nil {
178+
return newCopyError("CopyGraph", CopyErrorOriginSource, errors.New("nil source target"))
179+
}
180+
if dst == nil {
181+
return newCopyError("CopyGraph", CopyErrorOriginDestination, errors.New("nil destination target"))
182+
}
177183
return copyGraph(ctx, src, dst, root, nil, nil, nil, opts)
178184
}
179185

@@ -222,7 +228,7 @@ func copyGraph(ctx context.Context, src content.ReadOnlyStorage, dst content.Sto
222228
// skip if a rooted sub-DAG exists
223229
exists, err := dst.Exists(ctx, desc)
224230
if err != nil {
225-
return err
231+
return newCopyError("Exists", CopyErrorOriginDestination, err)
226232
}
227233
if exists {
228234
if opts.OnCopySkipped != nil {
@@ -236,7 +242,7 @@ func copyGraph(ctx context.Context, src content.ReadOnlyStorage, dst content.Sto
236242
// find successors while non-leaf nodes will be fetched and cached
237243
successors, err := opts.FindSuccessors(ctx, proxy, desc)
238244
if err != nil {
239-
return err
245+
return newCopyError("FindSuccessors", CopyErrorOriginSource, err)
240246
}
241247
successors = removeForeignLayers(successors)
242248

@@ -264,7 +270,7 @@ func copyGraph(ctx context.Context, src content.ReadOnlyStorage, dst content.Sto
264270

265271
exists, err = proxy.Cache.Exists(ctx, desc)
266272
if err != nil {
267-
return err
273+
return fmt.Errorf("failed to check cache existence: %s: %w", desc.Digest, err)
268274
}
269275
if exists {
270276
return copyNode(ctx, proxy.Cache, dst, desc, opts)
@@ -324,7 +330,7 @@ func mountOrCopyNode(ctx context.Context, src content.ReadOnlyStorage, dst conte
324330

325331
// Mount or copy
326332
if err := mounter.Mount(ctx, desc, sourceRepository, getContent); err != nil && !errors.Is(err, skipSource) {
327-
return err
333+
return newCopyError("Mount", CopyErrorOriginDestination, err)
328334
}
329335

330336
if !mountFailed {
@@ -352,12 +358,12 @@ func mountOrCopyNode(ctx context.Context, src content.ReadOnlyStorage, dst conte
352358
func doCopyNode(ctx context.Context, src content.ReadOnlyStorage, dst content.Storage, desc ocispec.Descriptor) error {
353359
rc, err := src.Fetch(ctx, desc)
354360
if err != nil {
355-
return err
361+
return newCopyError("Fetch", CopyErrorOriginSource, err)
356362
}
357363
defer rc.Close()
358364
err = dst.Push(ctx, desc, rc)
359365
if err != nil && !errors.Is(err, errdef.ErrAlreadyExists) {
360-
return err
366+
return newCopyError("Push", CopyErrorOriginDestination, err)
361367
}
362368
return nil
363369
}
@@ -389,13 +395,13 @@ func copyNode(ctx context.Context, src content.ReadOnlyStorage, dst content.Stor
389395
func copyCachedNodeWithReference(ctx context.Context, src *cas.Proxy, dst registry.ReferencePusher, desc ocispec.Descriptor, dstRef string) error {
390396
rc, err := src.FetchCached(ctx, desc)
391397
if err != nil {
392-
return err
398+
return newCopyError("Fetch", CopyErrorOriginSource, err)
393399
}
394400
defer rc.Close()
395401

396402
err = dst.PushReference(ctx, desc, rc, dstRef)
397403
if err != nil && !errors.Is(err, errdef.ErrAlreadyExists) {
398-
return err
404+
return newCopyError("PushReference", CopyErrorOriginDestination, err)
399405
}
400406
return nil
401407
}
@@ -404,7 +410,11 @@ func copyCachedNodeWithReference(ctx context.Context, src *cas.Proxy, dst regist
404410
func resolveRoot(ctx context.Context, src ReadOnlyTarget, srcRef string, proxy *cas.Proxy) (ocispec.Descriptor, error) {
405411
refFetcher, ok := src.(registry.ReferenceFetcher)
406412
if !ok {
407-
return src.Resolve(ctx, srcRef)
413+
desc, err := src.Resolve(ctx, srcRef)
414+
if err != nil {
415+
return ocispec.Descriptor{}, newCopyError("Resolve", CopyErrorOriginSource, err)
416+
}
417+
return desc, nil
408418
}
409419

410420
// optimize performance for ReferenceFetcher targets
@@ -414,7 +424,7 @@ func resolveRoot(ctx context.Context, src ReadOnlyTarget, srcRef string, proxy *
414424
}
415425
root, rc, err := refProxy.FetchReference(ctx, srcRef)
416426
if err != nil {
417-
return ocispec.Descriptor{}, err
427+
return ocispec.Descriptor{}, newCopyError("FetchReference", CopyErrorOriginSource, err)
418428
}
419429
defer rc.Close()
420430
// cache root if it is a non-leaf node
@@ -425,7 +435,7 @@ func resolveRoot(ctx context.Context, src ReadOnlyTarget, srcRef string, proxy *
425435
return nil, errors.New("fetching only root node expected")
426436
})
427437
if _, err = content.Successors(ctx, fetcher, root); err != nil {
428-
return ocispec.Descriptor{}, err
438+
return ocispec.Descriptor{}, newCopyError("Successors", CopyErrorOriginSource, err)
429439
}
430440

431441
// TODO: optimize special case where root is a leaf node (i.e. a blob)
@@ -434,7 +444,7 @@ func resolveRoot(ctx context.Context, src ReadOnlyTarget, srcRef string, proxy *
434444
}
435445

436446
// prepareCopy prepares the hooks for copy.
437-
func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Proxy, root ocispec.Descriptor, opts *CopyOptions) error {
447+
func prepareCopy(_ context.Context, dst Target, dstRef string, proxy *cas.Proxy, root ocispec.Descriptor, opts *CopyOptions) error {
438448
if refPusher, ok := dst.(registry.ReferencePusher); ok {
439449
// optimize performance for ReferencePusher targets
440450
preCopy := opts.PreCopy
@@ -467,7 +477,7 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox
467477
if content.Equal(desc, root) {
468478
// for root node, tag it after copying it
469479
if err := dst.Tag(ctx, root, dstRef); err != nil {
470-
return err
480+
return newCopyError("Tag", CopyErrorOriginDestination, err)
471481
}
472482
}
473483
if postCopy != nil {
@@ -499,7 +509,10 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox
499509
return err
500510
}
501511
}
502-
return dst.Tag(ctx, root, dstRef)
512+
if err := dst.Tag(ctx, root, dstRef); err != nil {
513+
return newCopyError("Tag", CopyErrorOriginDestination, err)
514+
}
515+
return nil
503516
}
504517

505518
return nil

0 commit comments

Comments
 (0)