From cd61916dc138aff7b85e0610d4c6e6cc3efb6b39 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Tue, 16 Jan 2024 07:13:30 +0000 Subject: [PATCH 01/13] bump oras-go Signed-off-by: Billy Zha --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index cc69096f8..f115678a1 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( golang.org/x/sync v0.6.0 golang.org/x/term v0.16.0 gopkg.in/yaml.v3 v3.0.1 - oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3 + oras.land/oras-go/v2 v2.3.1-0.20240111050452-3b1dd0e97000 ) require ( diff --git a/go.sum b/go.sum index eb5dc9f77..2de1768e3 100644 --- a/go.sum +++ b/go.sum @@ -41,3 +41,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3 h1:cF1ErV93cAw4i5/MNF4pW6ULHa27ig3oYGlxouzymY8= oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3/go.mod h1:W2rj9/rGtsTh9lmU3S0uq3iwvR6wNvUuD8nmOFmWBUY= +oras.land/oras-go/v2 v2.3.1-0.20240111050452-3b1dd0e97000 h1:tCh6spxJeREZx+phlJMk76/gcK3V02g56siet4NP180= +oras.land/oras-go/v2 v2.3.1-0.20240111050452-3b1dd0e97000/go.mod h1:JE7nRv2IHCiuu7kbH3UmDCp7bYD38yfnt1sl0lkLrwk= From 2271b5d5a5bc15bcf8f8cc196da4f7ba2e372a42 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Tue, 16 Jan 2024 08:42:19 +0000 Subject: [PATCH 02/13] feat: support mounted copy Signed-off-by: Billy Zha --- cmd/oras/internal/display/track/target.go | 9 +++++++++ cmd/oras/internal/option/common.go | 19 ++++++++++++------- cmd/oras/root/cp.go | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/cmd/oras/internal/display/track/target.go b/cmd/oras/internal/display/track/target.go index 5ff07efe3..10a4f202c 100644 --- a/cmd/oras/internal/display/track/target.go +++ b/cmd/oras/internal/display/track/target.go @@ -17,6 +17,7 @@ package track import ( "context" + "errors" "io" "os" @@ -66,6 +67,14 @@ func NewTarget(t oras.GraphTarget, actionPrompt, donePrompt string, tty *os.File return gt, nil } +// Mount makes graphTarget implement oras.Mounter. +func (t *graphTarget) Mount(ctx context.Context, desc ocispec.Descriptor, fromRepo string, getContent func() (io.ReadCloser, error)) error { + if mounter, ok := t.GraphTarget.(registry.Mounter); ok { + return mounter.Mount(ctx, desc, fromRepo, getContent) + } + return errors.New("not implemented") +} + // Push pushes the content to the base oras.GraphTarget with tracking. func (t *graphTarget) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error { r, err := managedReader(content, expected, t.manager, t.actionPrompt, t.donePrompt) diff --git a/cmd/oras/internal/option/common.go b/cmd/oras/internal/option/common.go index 7cb14a2c0..dd8a49a00 100644 --- a/cmd/oras/internal/option/common.go +++ b/cmd/oras/internal/option/common.go @@ -21,7 +21,6 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/pflag" - "golang.org/x/term" "oras.land/oras/internal/trace" ) @@ -54,12 +53,18 @@ func (opts *Common) Parse() error { // parseTTY gets target options from user input. func (opts *Common) parseTTY(f *os.File) error { - if !opts.noTTY { - if opts.Debug { - opts.noTTY = true - } else if term.IsTerminal(int(f.Fd())) { - opts.TTY = f - } + // open /dev/pts/2 and set to it + f, err := os.OpenFile("/dev/pts/2", os.O_RDWR, 0) + if err != nil { + return err } + opts.TTY = f + // if !opts.noTTY { + // if opts.Debug { + // opts.noTTY = true + // } else if term.IsTerminal(int(f.Fd())) { + // opts.TTY = f + // } + // } return nil } diff --git a/cmd/oras/root/cp.go b/cmd/oras/root/cp.go index 94eb52044..9d9987f93 100644 --- a/cmd/oras/root/cp.go +++ b/cmd/oras/root/cp.go @@ -29,6 +29,7 @@ import ( "oras.land/oras-go/v2" "oras.land/oras-go/v2/content" "oras.land/oras-go/v2/registry" + "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" "oras.land/oras/cmd/oras/internal/argument" "oras.land/oras/cmd/oras/internal/display" @@ -162,8 +163,15 @@ func doCopy(ctx context.Context, src oras.ReadOnlyGraphTarget, dst oras.GraphTar promptCopying = "Copying" promptCopied = "Copied " promptSkipped = "Skipped" + promptMounted = "Mounted" ) - + srcRepo, srcIsRemote := src.(*remote.Repository) + dstRepo, dstIsRemote := dst.(*remote.Repository) + if srcIsRemote && dstIsRemote && srcRepo.Reference.Registry == dstRepo.Reference.Registry { + extendedCopyOptions.MountFrom = func(ctx context.Context, desc ocispec.Descriptor) ([]string, error) { + return []string{srcRepo.Reference.Repository}, nil + } + } if opts.TTY == nil { // none TTY output extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { @@ -180,6 +188,10 @@ func doCopy(ctx context.Context, src oras.ReadOnlyGraphTarget, dst oras.GraphTar } return display.PrintStatus(desc, promptCopied, opts.Verbose) } + extendedCopyOptions.OnMounted = func(ctx context.Context, desc ocispec.Descriptor) error { + committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) + return display.PrintStatus(desc, promptMounted, opts.Verbose) + } } else { // TTY output tracked, err := track.NewTarget(dst, promptCopying, promptCopied, opts.TTY) @@ -198,6 +210,10 @@ func doCopy(ctx context.Context, src oras.ReadOnlyGraphTarget, dst oras.GraphTar return tracked.Prompt(desc, promptSkipped) }) } + extendedCopyOptions.OnMounted = func(ctx context.Context, desc ocispec.Descriptor) error { + committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) + return tracked.Prompt(desc, promptMounted) + } } var desc ocispec.Descriptor From de091c8eda82215bdccc6cc62f91ce65c1b1dd0d Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Tue, 16 Jan 2024 08:51:42 +0000 Subject: [PATCH 03/13] revert testing code Signed-off-by: Billy Zha --- cmd/oras/internal/option/common.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/cmd/oras/internal/option/common.go b/cmd/oras/internal/option/common.go index dd8a49a00..7cb14a2c0 100644 --- a/cmd/oras/internal/option/common.go +++ b/cmd/oras/internal/option/common.go @@ -21,6 +21,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/pflag" + "golang.org/x/term" "oras.land/oras/internal/trace" ) @@ -53,18 +54,12 @@ func (opts *Common) Parse() error { // parseTTY gets target options from user input. func (opts *Common) parseTTY(f *os.File) error { - // open /dev/pts/2 and set to it - f, err := os.OpenFile("/dev/pts/2", os.O_RDWR, 0) - if err != nil { - return err + if !opts.noTTY { + if opts.Debug { + opts.noTTY = true + } else if term.IsTerminal(int(f.Fd())) { + opts.TTY = f + } } - opts.TTY = f - // if !opts.noTTY { - // if opts.Debug { - // opts.noTTY = true - // } else if term.IsTerminal(int(f.Fd())) { - // opts.TTY = f - // } - // } return nil } From d5b7bb1a48967b669c7715f7d8715128bfd5295d Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Tue, 16 Jan 2024 09:12:29 +0000 Subject: [PATCH 04/13] fix e2e Signed-off-by: Billy Zha --- test/e2e/internal/utils/match/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/internal/utils/match/status.go b/test/e2e/internal/utils/match/status.go index b98656a22..c35ae0e1f 100644 --- a/test/e2e/internal/utils/match/status.go +++ b/test/e2e/internal/utils/match/status.go @@ -76,6 +76,7 @@ func newStateMachine(cmd string) *stateMachine { sm.addPath("Copying", "Copied") sm.addPath("Skipped") sm.addPath("Exists") + sm.addPath("Mounted") default: ginkgo.Fail("Unrecognized cmd name " + cmd) } From 33bf187e6ab6cf08a512688b1577dc21b4107e0f Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 18 Jan 2024 08:39:44 +0000 Subject: [PATCH 05/13] resolve comments Signed-off-by: Billy Zha --- cmd/oras/internal/display/track/target.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/oras/internal/display/track/target.go b/cmd/oras/internal/display/track/target.go index 10a4f202c..3475fe715 100644 --- a/cmd/oras/internal/display/track/target.go +++ b/cmd/oras/internal/display/track/target.go @@ -17,7 +17,6 @@ package track import ( "context" - "errors" "io" "os" @@ -69,10 +68,9 @@ func NewTarget(t oras.GraphTarget, actionPrompt, donePrompt string, tty *os.File // Mount makes graphTarget implement oras.Mounter. func (t *graphTarget) Mount(ctx context.Context, desc ocispec.Descriptor, fromRepo string, getContent func() (io.ReadCloser, error)) error { - if mounter, ok := t.GraphTarget.(registry.Mounter); ok { - return mounter.Mount(ctx, desc, fromRepo, getContent) - } - return errors.New("not implemented") + // should not be called if the underlying GraphTarget is not a mounter + mounter := t.GraphTarget.(registry.Mounter) + return mounter.Mount(ctx, desc, fromRepo, getContent) } // Push pushes the content to the base oras.GraphTarget with tracking. From b75a324385be9553ed3ee9f1eb00d88198a3cc63 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 18 Jan 2024 08:56:56 +0000 Subject: [PATCH 06/13] add e2e Signed-off-by: Billy Zha --- test/e2e/suite/command/cp.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index e09e6f61f..e33970d27 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -349,6 +349,15 @@ var _ = Describe("1.1 registry users:", func() { var _ = Describe("OCI spec 1.0 registry users:", func() { When("running `cp`", func() { + It("should copy an image artifact with mounting", Focus, func() { + repo := cpTestRepo("1.0-mount") + src := RegistryRef(FallbackHost, ArtifactRepo, foobar.Tag) + dst := RegistryRef(FallbackHost, repo, "") + out := ORAS("cp", src, dst, "-v").Exec() + Expect(out).Should(gbytes.Say("\nMounted fcde2b2edba5 bar")) + CompareRef(src, RegistryRef(FallbackHost, repo, foobar.Digest)) + }) + It("should copy an image artifact and its referrers from a registry to a fallback registry", func() { repo := cpTestRepo("to-fallback") stateKeys := append(append(foobar.ImageLayerStateKeys, foobar.ManifestStateKey, foobar.ImageReferrerConfigStateKeys[0]), foobar.ImageReferrersStateKeys...) From 6fff372c1b3254ae84e649a241c72a4915ad975c Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 18 Jan 2024 08:58:31 +0000 Subject: [PATCH 07/13] remove focus Signed-off-by: Billy Zha --- test/e2e/suite/command/cp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index e33970d27..f0e68de5f 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -349,7 +349,7 @@ var _ = Describe("1.1 registry users:", func() { var _ = Describe("OCI spec 1.0 registry users:", func() { When("running `cp`", func() { - It("should copy an image artifact with mounting", Focus, func() { + It("should copy an image artifact with mounting", func() { repo := cpTestRepo("1.0-mount") src := RegistryRef(FallbackHost, ArtifactRepo, foobar.Tag) dst := RegistryRef(FallbackHost, repo, "") From cb44ce6ca68c6eb41e81166fdc664f546c0dde52 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 18 Jan 2024 09:10:24 +0000 Subject: [PATCH 08/13] fix e2e Signed-off-by: Billy Zha --- test/e2e/suite/command/cp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index f0e68de5f..d122e9d45 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -354,7 +354,7 @@ var _ = Describe("OCI spec 1.0 registry users:", func() { src := RegistryRef(FallbackHost, ArtifactRepo, foobar.Tag) dst := RegistryRef(FallbackHost, repo, "") out := ORAS("cp", src, dst, "-v").Exec() - Expect(out).Should(gbytes.Say("\nMounted fcde2b2edba5 bar")) + Expect(out).Should(gbytes.Say("Mounted fcde2b2edba5 bar")) CompareRef(src, RegistryRef(FallbackHost, repo, foobar.Digest)) }) From ee9eb053923acff6c0153c9aa1a2dc935ab86b68 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 19 Jan 2024 02:09:27 +0000 Subject: [PATCH 09/13] add coverage Signed-off-by: Billy Zha --- .../internal/display/track/target_test.go | 34 +++---------------- 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/cmd/oras/internal/display/track/target_test.go b/cmd/oras/internal/display/track/target_test.go index 6622df531..fa2c19a7d 100644 --- a/cmd/oras/internal/display/track/target_test.go +++ b/cmd/oras/internal/display/track/target_test.go @@ -25,6 +25,7 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras-go/v2" "oras.land/oras-go/v2/content/memory" + "oras.land/oras-go/v2/registry/remote" "oras.land/oras/cmd/oras/internal/display/console/testutils" ) @@ -79,34 +80,7 @@ func Test_referenceGraphTarget_PushReference(t *testing.T) { } } -func Test_referenceGraphTarget_Prompt(t *testing.T) { - // prepare - pty, device, err := testutils.NewPty() - if err != nil { - t.Fatal(err) - } - defer device.Close() - content := []byte("test") - desc := ocispec.Descriptor{ - MediaType: "application/octet-stream", - Digest: digest.FromBytes(content), - Size: int64(len(content)), - } - // test - prompt := "prompt" - target, err := NewTarget(memory.New(), "action", "done", device) - if err != nil { - t.Fatal(err) - } - m := target.(*graphTarget).manager - if err := target.Prompt(desc, prompt); err != nil { - t.Fatal(err) - } - if err := m.Close(); err != nil { - t.Fatal(err) - } - // validate - if err = testutils.MatchPty(pty, device, prompt, desc.MediaType, "100.00%", desc.Digest.String()); err != nil { - t.Fatal(err) - } +func Test_referenceGraphTarget_Mount(t *testing.T) { + target := graphTarget{GraphTarget: &remote.Repository{}} + _ = target.Mount(context.Background(), ocispec.Descriptor{}, "", nil) } From ff26b40197cc9e0f5815cba0520ba5e01acec451 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 19 Jan 2024 08:49:19 +0000 Subject: [PATCH 10/13] add coverage Signed-off-by: Billy Zha --- cmd/oras/root/cp_test.go | 143 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 9 deletions(-) diff --git a/cmd/oras/root/cp_test.go b/cmd/oras/root/cp_test.go index 53161772c..c20bb6c87 100644 --- a/cmd/oras/root/cp_test.go +++ b/cmd/oras/root/cp_test.go @@ -1,3 +1,5 @@ +//go:build darwin || freebsd || linux || netbsd || openbsd || solaris + /* Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,37 +21,105 @@ import ( "bytes" "context" "fmt" + "net/http" + "net/http/httptest" + "net/url" "os" "testing" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "oras.land/oras-go/v2/content/memory" + "oras.land/oras-go/v2/registry/remote" "oras.land/oras/cmd/oras/internal/display/console/testutils" ) var ( - src *memory.Store - desc ocispec.Descriptor + memStore *memory.Store + memDesc ocispec.Descriptor + manifestConent = []byte(`{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","artifactType":"application/vnd.unknown.artifact.v1","config":{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="},"layers":[{"mediaType":"application/vnd.oci.empty.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2,"data":"e30="}]}`) + manifestDigest = "sha256:1bb053792feb8d8d590001c212f2defad9277e091d2aa868cde2879ff41abb1b" + configContent = []byte("{}") + configDigest = "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a" + configMediaType = "application/vnd.oci.empty.v1+json" + host string + repoFrom = "from" + repoTo = "to" ) func TestMain(m *testing.M) { - src = memory.New() + // memory store for testing + memStore = memory.New() content := []byte("test") r := bytes.NewReader(content) - desc = ocispec.Descriptor{ + memDesc = ocispec.Descriptor{ MediaType: "application/octet-stream", Digest: digest.FromBytes(content), Size: int64(len(content)), } - if err := src.Push(context.Background(), desc, r); err != nil { + if err := memStore.Push(context.Background(), memDesc, r); err != nil { fmt.Println("Setup failed:", err) os.Exit(1) } - if err := src.Tag(context.Background(), desc, desc.Digest.String()); err != nil { + if err := memStore.Tag(context.Background(), memDesc, memDesc.Digest.String()); err != nil { fmt.Println("Setup failed:", err) os.Exit(1) } + + // test server + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoFrom, manifestDigest) && + r.Method == http.MethodHead: + w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest) + w.Header().Set("Content-Length", fmt.Sprint(len(manifestConent))) + w.WriteHeader(http.StatusOK) + case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoFrom, manifestDigest) && + r.Method == http.MethodGet: + w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest) + w.Header().Set("Content-Length", fmt.Sprint(len(manifestConent))) + _, _ = w.Write(manifestConent) + w.WriteHeader(http.StatusOK) + case r.URL.Path == fmt.Sprintf("/v2/%s/blobs/%s", repoFrom, configDigest) && + r.Method == http.MethodGet: + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Length", fmt.Sprint(len(configContent))) + _, _ = w.Write(configContent) + w.WriteHeader(http.StatusOK) + case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoTo, manifestDigest) && + r.Method == http.MethodHead: + w.WriteHeader(http.StatusNotFound) + case r.URL.Path == fmt.Sprintf("/v2/%s/blobs/%s", repoTo, configDigest) && + r.Method == http.MethodHead: + w.WriteHeader(http.StatusNotFound) + case r.URL.Path == fmt.Sprintf("/v2/%s/blobs/uploads/", repoTo) && + r.URL.Query().Get("mount") == configDigest && + r.URL.Query().Get("from") == repoFrom && + r.Method == http.MethodPost: + w.Header().Set("Location", fmt.Sprintf("/v2/%s/blobs/%s", repoTo, configDigest)) + w.WriteHeader(http.StatusCreated) + // case r.URL.Path == fmt.Sprintf("/v2/%s/blobs/%s", repoTo, configDigest) && + // r.Method == http.MethodPut: + // // _, _ = io.ReadAll(r.Body) + // w.Header().Set("Docker-Content-Digest", configDigest) + // w.WriteHeader(http.StatusCreated) + + case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoTo, manifestDigest) && + r.Method == http.MethodPut: + w.WriteHeader(http.StatusCreated) + case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoTo, manifestDigest) && + r.Method == http.MethodGet: + w.Header().Set("Content-Type", ocispec.MediaTypeImageManifest) + w.Header().Set("Content-Length", fmt.Sprint(len(manifestConent))) + _, _ = w.Write(manifestConent) + w.WriteHeader(http.StatusOK) + default: + w.WriteHeader(http.StatusNotAcceptable) + } + })) + defer ts.Close() + uri, _ := url.Parse(ts.URL) + host = "localhost:" + uri.Port() m.Run() } @@ -63,15 +133,70 @@ func Test_doCopy(t *testing.T) { var opts copyOptions opts.TTY = slave opts.Verbose = true - opts.From.Reference = desc.Digest.String() + opts.From.Reference = memDesc.Digest.String() dst := memory.New() // test - _, err = doCopy(context.Background(), src, dst, opts) + _, err = doCopy(context.Background(), memStore, dst, opts) + if err != nil { + t.Fatal(err) + } + // validate + if err = testutils.MatchPty(pty, slave, "Copied", memDesc.MediaType, "100.00%", memDesc.Digest.String()); err != nil { + t.Fatal(err) + } +} + +func Test_doCopy_skipped(t *testing.T) { + // prepare + pty, slave, err := testutils.NewPty() + if err != nil { + t.Fatal(err) + } + defer slave.Close() + var opts copyOptions + opts.TTY = slave + opts.Verbose = true + opts.From.Reference = memDesc.Digest.String() + // test + _, err = doCopy(context.Background(), memStore, memStore, opts) + if err != nil { + t.Fatal(err) + } + // validate + if err = testutils.MatchPty(pty, slave, "Exists", memDesc.MediaType, "100.00%", memDesc.Digest.String()); err != nil { + t.Fatal(err) + } +} + +func Test_doCopy_mounted(t *testing.T) { + // prepare + pty, slave, err := testutils.NewPty() + if err != nil { + t.Fatal(err) + } + defer slave.Close() + var opts copyOptions + opts.TTY = slave + opts.Verbose = true + opts.From.Reference = manifestDigest + // mocked repositories + from, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, repoFrom)) + if err != nil { + t.Fatal(err) + } + from.PlainHTTP = true + to, err := remote.NewRepository(fmt.Sprintf("%s/%s", host, repoTo)) + if err != nil { + t.Fatal(err) + } + to.PlainHTTP = true + // test + _, err = doCopy(context.Background(), from, to, opts) if err != nil { t.Fatal(err) } // validate - if err = testutils.MatchPty(pty, slave, "Copied", desc.MediaType, "100.00%", desc.Digest.String()); err != nil { + if err = testutils.MatchPty(pty, slave, "Mounted", configMediaType, "100.00%", configDigest); err != nil { t.Fatal(err) } } From e5cecba1f02f4b4e5a75267507dd02e24e342673 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 19 Jan 2024 08:50:04 +0000 Subject: [PATCH 11/13] resolve coment Signed-off-by: Billy Zha --- cmd/oras/internal/display/track/target.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/oras/internal/display/track/target.go b/cmd/oras/internal/display/track/target.go index 3475fe715..3342f7ee2 100644 --- a/cmd/oras/internal/display/track/target.go +++ b/cmd/oras/internal/display/track/target.go @@ -66,9 +66,9 @@ func NewTarget(t oras.GraphTarget, actionPrompt, donePrompt string, tty *os.File return gt, nil } -// Mount makes graphTarget implement oras.Mounter. +// Mount mounts a blob from a specified repository. This method is invoked only +// by the `*remote.Repository` target. func (t *graphTarget) Mount(ctx context.Context, desc ocispec.Descriptor, fromRepo string, getContent func() (io.ReadCloser, error)) error { - // should not be called if the underlying GraphTarget is not a mounter mounter := t.GraphTarget.(registry.Mounter) return mounter.Mount(ctx, desc, fromRepo, getContent) } From f9273ddf7c0878ab99b913889bb8565c523192f8 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 19 Jan 2024 08:57:29 +0000 Subject: [PATCH 12/13] code clean Signed-off-by: Billy Zha --- cmd/oras/root/cp_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cmd/oras/root/cp_test.go b/cmd/oras/root/cp_test.go index c20bb6c87..69e5ae375 100644 --- a/cmd/oras/root/cp_test.go +++ b/cmd/oras/root/cp_test.go @@ -98,12 +98,6 @@ func TestMain(m *testing.M) { r.Method == http.MethodPost: w.Header().Set("Location", fmt.Sprintf("/v2/%s/blobs/%s", repoTo, configDigest)) w.WriteHeader(http.StatusCreated) - // case r.URL.Path == fmt.Sprintf("/v2/%s/blobs/%s", repoTo, configDigest) && - // r.Method == http.MethodPut: - // // _, _ = io.ReadAll(r.Body) - // w.Header().Set("Docker-Content-Digest", configDigest) - // w.WriteHeader(http.StatusCreated) - case r.URL.Path == fmt.Sprintf("/v2/%s/manifests/%s", repoTo, manifestDigest) && r.Method == http.MethodPut: w.WriteHeader(http.StatusCreated) From 45ac34b8624a23a927596b1207025282a5bf77d9 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 19 Jan 2024 09:21:33 +0000 Subject: [PATCH 13/13] clean go mod Signed-off-by: Billy Zha --- go.sum | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/go.sum b/go.sum index c3a702a20..1d4bfb01c 100644 --- a/go.sum +++ b/go.sum @@ -37,7 +37,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3 h1:cF1ErV93cAw4i5/MNF4pW6ULHa27ig3oYGlxouzymY8= -oras.land/oras-go/v2 v2.3.1-0.20231227022511-1d9ad6c409b3/go.mod h1:W2rj9/rGtsTh9lmU3S0uq3iwvR6wNvUuD8nmOFmWBUY= -oras.land/oras-go/v2 v2.3.1-0.20240111050452-3b1dd0e97000 h1:tCh6spxJeREZx+phlJMk76/gcK3V02g56siet4NP180= -oras.land/oras-go/v2 v2.3.1-0.20240111050452-3b1dd0e97000/go.mod h1:JE7nRv2IHCiuu7kbH3UmDCp7bYD38yfnt1sl0lkLrwk= +oras.land/oras-go/v2 v2.3.1-0.20240117093352-d8783fe25d71 h1:h7uU+S5uT4nJJr0dhgkARkid0XcAwcxveqBSSeYMR3A= +oras.land/oras-go/v2 v2.3.1-0.20240117093352-d8783fe25d71/go.mod h1:JE7nRv2IHCiuu7kbH3UmDCp7bYD38yfnt1sl0lkLrwk=