From 5362988eeab073194f7bbf0409330d9fd8380339 Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Tue, 19 Jul 2022 15:41:40 +0800 Subject: [PATCH 1/5] Tag existed root Signed-off-by: Wei Meng --- copy.go | 20 +++++++++++ copy_test.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/copy.go b/copy.go index 9fc66ff1..871ce23c 100644 --- a/copy.go +++ b/copy.go @@ -342,5 +342,25 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox } } + onCopySkipped := opts.OnCopySkipped + opts.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { + if onCopySkipped != nil { + if err := onCopySkipped(ctx, desc); err != nil { + return err + } + } + if !descriptor.EqualOCI(desc, root) { + return nil + } + // copy reference for skipped root + var err error + if refPusher, ok := dst.(registry.ReferencePusher); ok { + err = copyCachedNodeWithReference(ctx, proxy, refPusher, desc, dstRef) + } else { + err = dst.Tag(ctx, root, dstRef) + } + return err + } + return nil } diff --git a/copy_test.go b/copy_test.go index 5ac56726..70db50c2 100644 --- a/copy_test.go +++ b/copy_test.go @@ -135,6 +135,100 @@ func TestCopy_FullCopy(t *testing.T) { } } +func TestCopy_ExistedRoot(t *testing.T) { + src := memory.New() + dst := memory.New() + + // generate test content + var blobs [][]byte + var descs []ocispec.Descriptor + appendBlob := func(mediaType string, blob []byte) { + blobs = append(blobs, blob) + descs = append(descs, ocispec.Descriptor{ + MediaType: mediaType, + Digest: digest.FromBytes(blob), + Size: int64(len(blob)), + }) + } + generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { + manifest := ocispec.Manifest{ + Config: config, + Layers: layers, + } + manifestJSON, err := json.Marshal(manifest) + if err != nil { + t.Fatal(err) + } + appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) + } + + appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 + appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 + appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 + generateManifest(descs[0], descs[1:3]...) // Blob 3 + + ctx := context.Background() + for i := range blobs { + err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) + if err != nil { + t.Fatalf("failed to push test content to src: %d: %v", i, err) + } + } + + root := descs[3] + ref := "foobar" + newTag := "newtag" + err := src.Tag(ctx, root, ref) + if err != nil { + t.Fatal("fail to tag root node", err) + } + + // copy with src tag + gotDesc, err := oras.Copy(ctx, src, ref, dst, "", oras.CopyOptions{}) + if err != nil { + t.Fatalf("Copy() error = %v, wantErr %v", err, false) + } + if !reflect.DeepEqual(gotDesc, root) { + t.Errorf("Copy() = %v, want %v", gotDesc, root) + } + // copy with new tag + gotDesc, err = oras.Copy(ctx, src, ref, dst, newTag, oras.CopyOptions{}) + if err != nil { + t.Fatalf("Copy() error = %v, wantErr %v", err, false) + } + if !reflect.DeepEqual(gotDesc, root) { + t.Errorf("Copy() = %v, want %v", gotDesc, root) + } + + // verify contents + for i, desc := range descs { + exists, err := dst.Exists(ctx, desc) + if err != nil { + t.Fatalf("dst.Exists(%d) error = %v", i, err) + } + if !exists { + t.Errorf("dst.Exists(%d) = %v, want %v", i, exists, true) + } + } + + // verify src tag + gotDesc, err = dst.Resolve(ctx, ref) + if err != nil { + t.Fatal("dst.Resolve() error =", err) + } + if !reflect.DeepEqual(gotDesc, root) { + t.Errorf("dst.Resolve() = %v, want %v", gotDesc, root) + } + // verify new tag + gotDesc, err = dst.Resolve(ctx, newTag) + if err != nil { + t.Fatal("dst.Resolve() error =", err) + } + if !reflect.DeepEqual(gotDesc, root) { + t.Errorf("dst.Resolve() = %v, want %v", gotDesc, root) + } +} + func TestCopyGraph_FullCopy(t *testing.T) { src := cas.NewMemory() dst := cas.NewMemory() From 16131ccf0c7899cc66b8323e3454aef39ddb995d Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Wed, 20 Jul 2022 11:14:11 +0800 Subject: [PATCH 2/5] UT Signed-off-by: Wei Meng --- copy_test.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/copy_test.go b/copy_test.go index 70db50c2..c8c36714 100644 --- a/copy_test.go +++ b/copy_test.go @@ -183,8 +183,18 @@ func TestCopy_ExistedRoot(t *testing.T) { t.Fatal("fail to tag root node", err) } + var skippedCount int64 + copyOpts := oras.CopyOptions{ + CopyGraphOptions: oras.CopyGraphOptions{ + OnCopySkipped: func(ctx context.Context, desc ocispec.Descriptor) error { + atomic.AddInt64(&skippedCount, 1) + return nil + }, + }, + } + // copy with src tag - gotDesc, err := oras.Copy(ctx, src, ref, dst, "", oras.CopyOptions{}) + gotDesc, err := oras.Copy(ctx, src, ref, dst, "", copyOpts) if err != nil { t.Fatalf("Copy() error = %v, wantErr %v", err, false) } @@ -192,7 +202,7 @@ func TestCopy_ExistedRoot(t *testing.T) { t.Errorf("Copy() = %v, want %v", gotDesc, root) } // copy with new tag - gotDesc, err = oras.Copy(ctx, src, ref, dst, newTag, oras.CopyOptions{}) + gotDesc, err = oras.Copy(ctx, src, ref, dst, newTag, copyOpts) if err != nil { t.Fatalf("Copy() error = %v, wantErr %v", err, false) } @@ -227,6 +237,10 @@ func TestCopy_ExistedRoot(t *testing.T) { if !reflect.DeepEqual(gotDesc, root) { t.Errorf("dst.Resolve() = %v, want %v", gotDesc, root) } + // verify invocation of onCopySkipped() + if got, want := skippedCount, int64(1); got != want { + t.Errorf("count(OnCopySkipped()) = %v, want %v", got, want) + } } func TestCopyGraph_FullCopy(t *testing.T) { From a1e96375bf6e8dbb0da40f2f16fd481e313b79e4 Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Wed, 20 Jul 2022 11:20:35 +0800 Subject: [PATCH 3/5] Cleanup Signed-off-by: Wei Meng --- copy.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/copy.go b/copy.go index 871ce23c..98af7b81 100644 --- a/copy.go +++ b/copy.go @@ -353,13 +353,11 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox return nil } // copy reference for skipped root - var err error if refPusher, ok := dst.(registry.ReferencePusher); ok { - err = copyCachedNodeWithReference(ctx, proxy, refPusher, desc, dstRef) + return copyCachedNodeWithReference(ctx, proxy, refPusher, desc, dstRef) } else { - err = dst.Tag(ctx, root, dstRef) + return dst.Tag(ctx, root, dstRef) } - return err } return nil From 3072b1be8cf7c87aac1e7832c982b03f7142bcd6 Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Wed, 20 Jul 2022 12:48:59 +0800 Subject: [PATCH 4/5] Apply suggestions from code review Signed-off-by: Wei Meng --- copy.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/copy.go b/copy.go index 98af7b81..5ebf5924 100644 --- a/copy.go +++ b/copy.go @@ -352,12 +352,11 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox if !descriptor.EqualOCI(desc, root) { return nil } - // copy reference for skipped root + // enforce tagging when root manifest is skipped if refPusher, ok := dst.(registry.ReferencePusher); ok { return copyCachedNodeWithReference(ctx, proxy, refPusher, desc, dstRef) - } else { - return dst.Tag(ctx, root, dstRef) } + return dst.Tag(ctx, root, dstRef) } return nil From aa76d41f83ed3e70a2c94275be7da4b59659bab8 Mon Sep 17 00:00:00 2001 From: Wei Meng Date: Wed, 20 Jul 2022 14:33:07 +0800 Subject: [PATCH 5/5] Comment Signed-off-by: Wei Meng --- copy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/copy.go b/copy.go index 5ebf5924..66f4d7e9 100644 --- a/copy.go +++ b/copy.go @@ -352,7 +352,7 @@ func prepareCopy(ctx context.Context, dst Target, dstRef string, proxy *cas.Prox if !descriptor.EqualOCI(desc, root) { return nil } - // enforce tagging when root manifest is skipped + // enforce tagging when root is skipped if refPusher, ok := dst.(registry.ReferencePusher); ok { return copyCachedNodeWithReference(ctx, proxy, refPusher, desc, dstRef) }