From ab5611667c67c93d1046494b9b587d7c83aef119 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 13 Mar 2023 12:59:22 +0800 Subject: [PATCH] test(e2e): add OCI layout e2e specs for `oras cp` (#869) Signed-off-by: Billy Zha --- .../e2e/internal/testdata/multi_arch/const.go | 16 +- test/e2e/internal/utils/flags.go | 14 +- test/e2e/suite/command/blob.go | 24 +- test/e2e/suite/command/cp.go | 306 +++++++++++++++++- test/e2e/suite/command/pull.go | 2 +- 5 files changed, 328 insertions(+), 34 deletions(-) diff --git a/test/e2e/internal/testdata/multi_arch/const.go b/test/e2e/internal/testdata/multi_arch/const.go index 34d5cfb2f..799f897fb 100644 --- a/test/e2e/internal/testdata/multi_arch/const.go +++ b/test/e2e/internal/testdata/multi_arch/const.go @@ -22,12 +22,14 @@ import ( ) var ( - Tag = "multi" - Digest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" - Manifest = `{"mediaType":"application/vnd.oci.image.index.v1+json","schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458,"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:4f93460061882467e6fb3b772dc6ab72130d9ac1906aed2fc7589a5cd145433c","size":458,"platform":{"architecture":"arm64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:58efe73e78fe043ca31b89007a025c594ce12aa7e6da27d21c7b14b50112e255","size":458,"platform":{"architecture":"arm","os":"linux","variant":"v7"}}]}` - Descriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` - IndexReferrerDigest = "sha256:d3cf790759b006e1a2aeee52f9b1ee250bb848fce7e873b992b86bf9408f12d0" - IndexStateKeys = []match.StateKey{ + Tag = "multi" + Digest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" + Manifest = `{"mediaType":"application/vnd.oci.image.index.v1+json","schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458,"platform":{"architecture":"amd64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:4f93460061882467e6fb3b772dc6ab72130d9ac1906aed2fc7589a5cd145433c","size":458,"platform":{"architecture":"arm64","os":"linux"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:58efe73e78fe043ca31b89007a025c594ce12aa7e6da27d21c7b14b50112e255","size":458,"platform":{"architecture":"arm","os":"linux","variant":"v7"}}]}` + Descriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` + IndexReferrerDigest = "sha256:d3cf790759b006e1a2aeee52f9b1ee250bb848fce7e873b992b86bf9408f12d0" + IndexReferrerStateKey = match.StateKey{Digest: "d3cf790759b0", Name: "application/vnd.oci.image.manifest.v1+json"} + IndexReferrerConfigStateKey = match.StateKey{Digest: "44136fa355b3", Name: "referrer.index"} + IndexStateKeys = []match.StateKey{ {Digest: "2ef548696ac7", Name: "hello.tar"}, {Digest: "fe9dbc99451d", Name: "application/vnd.oci.image.config.v1+json"}, {Digest: "9d84a5716c66", Name: "application/vnd.oci.image.manifest.v1+json"}, @@ -59,7 +61,7 @@ var ( LayerName = "hello.tar" LinuxAMD64ReferrerStateKey = match.StateKey{Digest: "57e6462826c8", Name: "application/vnd.oci.image.manifest.v1+json"} LinuxAMD64ReferrerConfigStateKey = match.StateKey{Digest: "44136fa355b3", Name: "referrer.image"} - ImageStateKeys = []match.StateKey{ + LinuxAMD64StateKeys = []match.StateKey{ {Digest: "9d84a5716c66", Name: ocispec.MediaTypeImageManifest}, {Digest: "fe9dbc99451d", Name: ocispec.MediaTypeImageConfig}, {Digest: "2ef548696ac7", Name: "hello.tar"}, diff --git a/test/e2e/internal/utils/flags.go b/test/e2e/internal/utils/flags.go index 0f7ac15a1..8263e49f9 100644 --- a/test/e2e/internal/utils/flags.go +++ b/test/e2e/internal/utils/flags.go @@ -15,8 +15,12 @@ limitations under the License. package utils -const ( - LayoutFlag = "--oci-layout" - FromLayout = "--from-oci-layout" - ToLayout = "--to-oci-layout" -) +var Flags = struct { + Layout string + FromLayout string + ToLayout string +}{ + "--oci-layout", + "--from-oci-layout", + "--to-oci-layout", +} diff --git a/test/e2e/suite/command/blob.go b/test/e2e/suite/command/blob.go index bb6a6e96a..15f13f408 100644 --- a/test/e2e/suite/command/blob.go +++ b/test/e2e/suite/command/blob.go @@ -250,15 +250,15 @@ var _ = Describe("Common registry users:", func() { var _ = Describe("OCI image layout users:", func() { prepare := func(from string) string { tmpRoot := GinkgoT().TempDir() - ORAS("cp", from, ToLayout, tmpRoot).WithDescription("prepare image from registry to OCI layout").Exec() + ORAS("cp", from, Flags.ToLayout, tmpRoot).WithDescription("prepare image from registry to OCI layout").Exec() return tmpRoot } When("running `blob delete`", func() { It("should not support deleting a blob", func() { toDeleteRef := LayoutRef(prepare(RegistryRef(Host, ImageRepo, foobar.Tag)), foobar.FooBlobDigest) - ORAS("blob", "delete", LayoutFlag, toDeleteRef). + ORAS("blob", "delete", Flags.Layout, toDeleteRef). WithInput(strings.NewReader("y")). - MatchErrKeyWords("Error:", "unknown flag", LayoutFlag). + MatchErrKeyWords("Error:", "unknown flag", Flags.Layout). ExpectFailure(). Exec() }) @@ -267,19 +267,19 @@ var _ = Describe("OCI image layout users:", func() { When("running `blob fetch`", func() { It("should fetch blob descriptor", func() { root := prepare(RegistryRef(Host, ImageRepo, foobar.Tag)) - ORAS("blob", "fetch", LayoutFlag, LayoutRef(root, foobar.FooBlobDigest), "--descriptor"). + ORAS("blob", "fetch", Flags.Layout, LayoutRef(root, foobar.FooBlobDigest), "--descriptor"). MatchContent(foobar.FooBlobDescriptor).Exec() }) It("should fetch blob content and output to stdout", func() { root := prepare(RegistryRef(Host, ImageRepo, foobar.Tag)) - ORAS("blob", "fetch", LayoutFlag, LayoutRef(root, foobar.FooBlobDigest), "--output", "-"). + ORAS("blob", "fetch", Flags.Layout, LayoutRef(root, foobar.FooBlobDigest), "--output", "-"). MatchContent(foobar.FooBlobContent).Exec() }) It("should fetch blob content and output to a file", func() { root := prepare(RegistryRef(Host, ImageRepo, foobar.Tag)) tempDir := GinkgoT().TempDir() contentPath := filepath.Join(tempDir, "fetched") - ORAS("blob", "fetch", LayoutFlag, LayoutRef(root, foobar.FooBlobDigest), "--output", contentPath). + ORAS("blob", "fetch", Flags.Layout, LayoutRef(root, foobar.FooBlobDigest), "--output", contentPath). WithWorkDir(tempDir).Exec() MatchFile(contentPath, foobar.FooBlobContent, DefaultTimeout) }) @@ -287,7 +287,7 @@ var _ = Describe("OCI image layout users:", func() { root := prepare(RegistryRef(Host, ImageRepo, foobar.Tag)) tempDir := GinkgoT().TempDir() contentPath := filepath.Join(tempDir, "fetched") - ORAS("blob", "fetch", LayoutFlag, LayoutRef(root, foobar.FooBlobDigest), "--output", contentPath, "--descriptor"). + ORAS("blob", "fetch", Flags.Layout, LayoutRef(root, foobar.FooBlobDigest), "--output", contentPath, "--descriptor"). MatchContent(foobar.FooBlobDescriptor). WithWorkDir(tempDir).Exec() MatchFile(contentPath, foobar.FooBlobContent, DefaultTimeout) @@ -301,13 +301,13 @@ var _ = Describe("OCI image layout users:", func() { mediaType := "test.media" blobPath := WriteTempFile("blob", pushContent) // test - ORAS("blob", "push", LayoutFlag, LayoutRef(tmpRoot, pushDigest), blobPath, "--media-type", mediaType, "--descriptor"). + ORAS("blob", "push", Flags.Layout, LayoutRef(tmpRoot, pushDigest), blobPath, "--media-type", mediaType, "--descriptor"). MatchContent(fmt.Sprintf(pushDescFmt, mediaType)).Exec() - ORAS("blob", "push", LayoutFlag, LayoutRef(tmpRoot, pushDigest), blobPath, "-v"). + ORAS("blob", "push", Flags.Layout, LayoutRef(tmpRoot, pushDigest), blobPath, "-v"). WithDescription("skip pushing if the blob already exists in the target repo"). MatchKeyWords("Exists").Exec() // validate - ORAS("blob", "fetch", LayoutRef(tmpRoot, pushDigest), LayoutFlag, "--output", "-").MatchContent(pushContent).Exec() + ORAS("blob", "fetch", LayoutRef(tmpRoot, pushDigest), Flags.Layout, "--output", "-").MatchContent(pushContent).Exec() }) It("should push a blob from a stdin and output the descriptor with specific media-type", func() { @@ -315,11 +315,11 @@ var _ = Describe("OCI image layout users:", func() { tmpRoot := GinkgoT().TempDir() // test mediaType := "test.media" - ORAS("blob", "push", LayoutFlag, LayoutRef(tmpRoot, pushDigest), "-", "--media-type", mediaType, "--descriptor", "--size", strconv.Itoa(len(pushContent))). + ORAS("blob", "push", Flags.Layout, LayoutRef(tmpRoot, pushDigest), "-", "--media-type", mediaType, "--descriptor", "--size", strconv.Itoa(len(pushContent))). WithInput(strings.NewReader(pushContent)). MatchContent(fmt.Sprintf(pushDescFmt, mediaType)).Exec() // validate - ORAS("blob", "fetch", LayoutRef(tmpRoot, pushDigest), LayoutFlag, "--output", "-").MatchContent(pushContent).Exec() + ORAS("blob", "fetch", LayoutRef(tmpRoot, pushDigest), Flags.Layout, "--output", "-").MatchContent(pushContent).Exec() }) }) }) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index bab07e22a..4b49187fa 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -63,14 +63,13 @@ var _ = Describe("ORAS beginners:", func() { var foobarStates = append(foobar.ImageLayerStateKeys, foobar.ManifestStateKey, foobar.ImageConfigStateKey(oras.MediaTypeUnknownConfig)) func CompareRef(src, dst string) { - srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source for validation").Exec().Out.Contents() - dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination for validation").Exec().Out.Contents() + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() Expect(srcManifest).To(Equal(dstManifest)) } var _ = Describe("Common registry users:", func() { When("running `cp`", func() { - It("should copy an image to a new repository via tag", func() { src := RegistryRef(Host, ImageRepo, foobar.Tag) dst := RegistryRef(Host, cpTestRepo("tag"), "copiedTag") @@ -101,11 +100,12 @@ var _ = Describe("Common registry users:", func() { }) It("should copy a multi-arch image and its referrers to a new repository via tag", func() { + stateKeys := append(ma.IndexStateKeys, ma.IndexReferrerStateKey, ma.IndexReferrerConfigStateKey) src := RegistryRef(Host, ArtifactRepo, ma.Tag) dstRepo := cpTestRepo("index-referrers") dst := RegistryRef(Host, dstRepo, "copiedTag") ORAS("cp", src, dst, "-r", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(stateKeys, true, len(stateKeys)). MatchKeyWords("Digest: " + ma.Digest). Exec() // validate @@ -125,11 +125,12 @@ var _ = Describe("Common registry users:", func() { }) It("should copy a multi-arch image and its referrers to a new repository via digest", func() { + stateKeys := append(ma.IndexStateKeys, ma.IndexReferrerStateKey, ma.IndexReferrerConfigStateKey) src := RegistryRef(Host, ArtifactRepo, ma.Tag) dstRepo := cpTestRepo("index-referrers-digest") dst := RegistryRef(Host, dstRepo, ma.Digest) ORAS("cp", src, dst, "-r", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(stateKeys, true, len(stateKeys)). MatchKeyWords("Digest: " + ma.Digest). Exec() // validate @@ -153,7 +154,7 @@ var _ = Describe("Common registry users:", func() { dst := RegistryRef(Host, cpTestRepo("platform-tag"), "copiedTag") ORAS("cp", src, dst, "--platform", "linux/amd64", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(ma.LinuxAMD64StateKeys, true, len(ma.LinuxAMD64StateKeys)). MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). Exec() CompareRef(RegistryRef(Host, ImageRepo, ma.LinuxAMD64.Digest.String()), dst) @@ -164,18 +165,19 @@ var _ = Describe("Common registry users:", func() { dstRepo := cpTestRepo("platform-digest") dst := RegistryRef(Host, dstRepo, "") ORAS("cp", src, dst, "--platform", "linux/amd64", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(ma.LinuxAMD64StateKeys, true, len(ma.LinuxAMD64StateKeys)). MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). Exec() CompareRef(RegistryRef(Host, ImageRepo, ma.LinuxAMD64.Digest.String()), RegistryRef(Host, dstRepo, ma.LinuxAMD64.Digest.String())) }) It("should copy a certain platform of image and its referrers to a new repository with tag", func() { + stateKeys := append(ma.LinuxAMD64StateKeys, ma.LinuxAMD64ReferrerStateKey, ma.LinuxAMD64ReferrerConfigStateKey) src := RegistryRef(Host, ArtifactRepo, ma.Tag) dstRepo := cpTestRepo("platform-referrers") dst := RegistryRef(Host, dstRepo, "copiedTag") ORAS("cp", src, dst, "-r", "--platform", "linux/amd64", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(stateKeys, true, len(stateKeys)). MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). Exec() // validate @@ -199,10 +201,11 @@ var _ = Describe("Common registry users:", func() { }) It("should copy a certain platform of image and its referrers to a new repository without tagging", func() { + stateKeys := append(ma.LinuxAMD64StateKeys, ma.LinuxAMD64ReferrerStateKey, ma.LinuxAMD64ReferrerConfigStateKey) src := RegistryRef(Host, ArtifactRepo, ma.Tag) dstRepo := cpTestRepo("platform-referrers-no-tag") ORAS("cp", src, RegistryRef(Host, dstRepo, ""), "-r", "--platform", "linux/amd64", "-v"). - MatchStatus(ma.IndexStateKeys, true, len(ma.IndexStateKeys)). + MatchStatus(stateKeys, true, len(stateKeys)). MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). Exec() // validate @@ -262,5 +265,290 @@ var _ = Describe("OCI spec 1.0 registry users:", func() { ORAS("discover", "-o", "tree", RegistryRef(Host, repo, foobar.Digest)). WithDescription("discover referrer via subject").MatchKeyWords(foobar.FallbackSignatureImageReferrer.Digest.String(), foobar.FallbackSBOMImageReferrer.Digest.String()).Exec() }) + + It("should copy an image from a fallback registry to an OCI image layout via digest", func() { + dstDir := GinkgoT().TempDir() + src := RegistryRef(FallbackHost, ArtifactRepo, foobar.Tag) + ORAS("cp", src, dstDir, "-v", Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", LayoutRef(dstDir, foobar.Digest), Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image from an OCI image layout to a fallback registry via digest", func() { + layoutDir := GinkgoT().TempDir() + src := LayoutRef(layoutDir, foobar.Digest) + dst := RegistryRef(FallbackHost, cpTestRepo("from-layout-digest"), "copied") + // prepare + ORAS("cp", RegistryRef(FallbackHost, ArtifactRepo, foobar.Tag), layoutDir, Flags.ToLayout).Exec() + // test + ORAS("cp", src, dst, "-v", Flags.FromLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy a certain platform of image and its referrers from an OCI image layout to a fallback registry", func() { + stateKeys := append(ma.LinuxAMD64StateKeys, ma.LinuxAMD64ReferrerStateKey, ma.LinuxAMD64ReferrerConfigStateKey) + fromDir := GinkgoT().TempDir() + src := LayoutRef(fromDir, ma.Tag) + dstRepo := cpTestRepo("platform-referrer-from-layout") + dst := RegistryRef(FallbackHost, dstRepo, "copied") + // prepare + ORAS("cp", RegistryRef(Host, ArtifactRepo, ma.Tag), src, Flags.ToLayout, "-r").Exec() + ORAS("cp", RegistryRef(Host, ArtifactRepo, ma.Tag), src, Flags.ToLayout, "-r", "--platform", "linux/amd64").Exec() + // test + ORAS("cp", src, Flags.FromLayout, dst, "-r", "-v", "--platform", "linux/amd64"). + MatchStatus(stateKeys, true, len(stateKeys)). + MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). + Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout, "--platform", "linux/amd64").WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + ORAS("manifest", "fetch", RegistryRef(FallbackHost, dstRepo, ma.Digest)).WithDescription("not copy index").ExpectFailure().Exec() + var index ocispec.Index + bytes := ORAS("discover", dst, "-o", "json"). + MatchKeyWords(ma.LinuxAMD64Referrer.Digest.String()). + WithDescription("copy image referrer"). + Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].Digest.String()).To(Equal(ma.LinuxAMD64Referrer.Digest.String())) + }) + }) +}) + +var _ = Describe("OCI layout users:", func() { + When("running `cp`", func() { + It("should copy an image from a registry to an OCI image layout via tag", func() { + dst := LayoutRef(GinkgoT().TempDir(), "copied") + src := RegistryRef(Host, ImageRepo, foobar.Tag) + ORAS("cp", src, dst, "-v", Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image from an OCI image layout to a registry via tag", func() { + layoutDir := GinkgoT().TempDir() + src := LayoutRef(layoutDir, "copied") + dst := RegistryRef(Host, cpTestRepo("from-layout-tag"), foobar.Tag) + // prepare + ORAS("cp", RegistryRef(Host, ImageRepo, foobar.Tag), src, Flags.ToLayout).Exec() + // test + ORAS("cp", src, dst, "-v", Flags.FromLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image between OCI image layouts via tag", func() { + srcDir := GinkgoT().TempDir() + toDir := GinkgoT().TempDir() + src := LayoutRef(srcDir, "from") + dst := LayoutRef(toDir, "to") + // prepare + ORAS("cp", RegistryRef(Host, ImageRepo, foobar.Tag), src, Flags.ToLayout).Exec() + // test + ORAS("cp", src, dst, "-v", Flags.FromLayout, Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image from a registry to an OCI image layout via digest", func() { + dstDir := GinkgoT().TempDir() + src := RegistryRef(Host, ImageRepo, foobar.Tag) + ORAS("cp", src, dstDir, "-v", Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", LayoutRef(dstDir, foobar.Digest), Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image from an OCI image layout to a registry via digest", func() { + layoutDir := GinkgoT().TempDir() + src := LayoutRef(layoutDir, foobar.Digest) + dst := RegistryRef(Host, cpTestRepo("from-layout-digest"), "copied") + // prepare + ORAS("cp", RegistryRef(Host, ImageRepo, foobar.Tag), layoutDir, Flags.ToLayout).Exec() + // test + ORAS("cp", src, dst, "-v", Flags.FromLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image between OCI image layouts via digest", func() { + srcDir := GinkgoT().TempDir() + toDir := GinkgoT().TempDir() + src := LayoutRef(srcDir, foobar.Digest) + dst := LayoutRef(toDir, foobar.Digest) + // prepare + ORAS("cp", RegistryRef(Host, ImageRepo, foobar.Tag), srcDir, Flags.ToLayout).Exec() + // test + ORAS("cp", src, toDir, "-v", Flags.FromLayout, Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy an image from a registry to an OCI image layout with multiple tagging", func() { + dstDir := GinkgoT().TempDir() + src := RegistryRef(Host, ImageRepo, foobar.Tag) + tags := []string{"tag1", "tag2", "tag3"} + // test + ORAS("cp", src, dstDir+":"+strings.Join(tags, ","), "-v", Flags.ToLayout).MatchStatus(foobarStates, true, len(foobarStates)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + for _, tag := range tags { + dstManifest := ORAS("manifest", "fetch", LayoutRef(dstDir, tag), Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + } + }) + + It("should copy a tagged image and its referrers from a registry to an OCI image layout", func() { + stateKeys := append(append(foobarStates, foobar.ArtifactReferrerStateKeys...), foobar.ImageReferrerConfigStateKeys...) + dst := LayoutRef(GinkgoT().TempDir(), "copied") + src := RegistryRef(Host, ArtifactRepo, foobar.Tag) + // test + ORAS("cp", "-r", src, dst, "-v", Flags.ToLayout).MatchStatus(stateKeys, true, len(stateKeys)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy a image and its referrers from a registry to an OCI image layout via digest", func() { + stateKeys := append(append(foobarStates, foobar.ArtifactReferrerStateKeys...), foobar.ImageReferrerConfigStateKeys...) + toDir := GinkgoT().TempDir() + src := RegistryRef(Host, ArtifactRepo, foobar.Digest) + // test + ORAS("cp", "-r", src, toDir, "-v", Flags.ToLayout).MatchStatus(stateKeys, true, len(stateKeys)).Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", LayoutRef(toDir, foobar.Digest), Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + }) + + It("should copy a multi-arch image and its referrers from a registry to an OCI image layout a via tag", func() { + stateKeys := append(ma.IndexStateKeys, ma.IndexReferrerStateKey, ma.IndexReferrerConfigStateKey) + src := RegistryRef(Host, ArtifactRepo, ma.Tag) + toDir := GinkgoT().TempDir() + dst := LayoutRef(toDir, "copied") + // test + ORAS("cp", src, Flags.ToLayout, dst, "-r", "-v"). + MatchStatus(stateKeys, true, len(stateKeys)). + MatchKeyWords("Digest: " + ma.Digest). + Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + var index ocispec.Index + bytes := ORAS("discover", dst, "-o", "json", Flags.Layout). + MatchKeyWords(ma.IndexReferrerDigest). + WithDescription("copy image referrer"). + Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].Digest.String()).To(Equal(ma.IndexReferrerDigest)) + ORAS("manifest", "fetch", Flags.Layout, LayoutRef(toDir, ma.LinuxAMD64Referrer.Digest.String())). + WithDescription("not copy referrer of successor"). + ExpectFailure(). + Exec() + }) + + It("should copy a multi-arch image and its referrers from an OCI image layout to a registry via digest", func() { + stateKeys := append(ma.IndexStateKeys, ma.IndexReferrerStateKey, ma.IndexReferrerConfigStateKey) + fromDir := GinkgoT().TempDir() + src := LayoutRef(fromDir, ma.Tag) + dst := RegistryRef(Host, cpTestRepo("recursive-from-layout"), "copied") + // prepare + ORAS("cp", RegistryRef(Host, ArtifactRepo, ma.Tag), src, Flags.ToLayout, "-r").Exec() + // test + ORAS("cp", src, Flags.FromLayout, dst, "-r", "-v"). + MatchStatus(stateKeys, true, len(stateKeys)). + MatchKeyWords("Digest: " + ma.Digest). + Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout).WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + var index ocispec.Index + bytes := ORAS("discover", dst, "-o", "json"). + MatchKeyWords(ma.IndexReferrerDigest). + WithDescription("copy image referrer"). + Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].Digest.String()).To(Equal(ma.IndexReferrerDigest)) + ORAS("manifest", "fetch", LayoutRef(fromDir, ma.LinuxAMD64Referrer.Digest.String())). + WithDescription("not copy referrer of successor"). + ExpectFailure(). + Exec() + }) + + It("should copy a certain platform of image and its referrers from an OCI image layout to a registry", func() { + stateKeys := append(ma.LinuxAMD64StateKeys, ma.LinuxAMD64ReferrerStateKey, ma.LinuxAMD64ReferrerConfigStateKey) + fromDir := GinkgoT().TempDir() + src := LayoutRef(fromDir, ma.Tag) + dstRepo := cpTestRepo("platform-referrer-from-layout") + dst := RegistryRef(Host, dstRepo, "copied") + // prepare + ORAS("cp", RegistryRef(Host, ArtifactRepo, ma.Tag), src, Flags.ToLayout, "-r").Exec() + ORAS("cp", RegistryRef(Host, ArtifactRepo, ma.Tag), src, Flags.ToLayout, "-r", "--platform", "linux/amd64").Exec() + // test + ORAS("cp", src, Flags.FromLayout, dst, "-r", "-v", "--platform", "linux/amd64"). + MatchStatus(stateKeys, true, len(stateKeys)). + MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). + Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, Flags.Layout, "--platform", "linux/amd64").WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + ORAS("manifest", "fetch", RegistryRef(Host, dstRepo, ma.Digest)).WithDescription("not copy index").ExpectFailure().Exec() + var index ocispec.Index + bytes := ORAS("discover", dst, "-o", "json"). + MatchKeyWords(ma.LinuxAMD64Referrer.Digest.String()). + WithDescription("copy image referrer"). + Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].Digest.String()).To(Equal(ma.LinuxAMD64Referrer.Digest.String())) + }) + + It("should copy a certain platform of image and its referrers from a registry to an OCI image layout", func() { + stateKeys := append(ma.LinuxAMD64StateKeys, ma.LinuxAMD64ReferrerStateKey, ma.LinuxAMD64ReferrerConfigStateKey) + src := RegistryRef(Host, ArtifactRepo, ma.Tag) + toDir := GinkgoT().TempDir() + dst := LayoutRef(toDir, "copied") + // test + ORAS("cp", src, Flags.ToLayout, dst, "-r", "-v", "--platform", "linux/amd64"). + MatchStatus(stateKeys, true, len(stateKeys)). + MatchKeyWords("Digest: " + ma.LinuxAMD64.Digest.String()). + Exec() + // validate + srcManifest := ORAS("manifest", "fetch", src, "--platform", "linux/amd64").WithDescription("fetch from source to validate").Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst, Flags.Layout).WithDescription("fetch from destination to validate").Exec().Out.Contents() + Expect(srcManifest).To(Equal(dstManifest)) + ORAS("manifest", "fetch", LayoutRef(toDir, ma.Digest)).WithDescription("not copy index").ExpectFailure().Exec() + var index ocispec.Index + bytes := ORAS("discover", dst, "-o", "json", Flags.Layout). + MatchKeyWords(ma.LinuxAMD64Referrer.Digest.String()). + WithDescription("copy image referrer"). + Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].Digest.String()).To(Equal(ma.LinuxAMD64Referrer.Digest.String())) + }) }) }) diff --git a/test/e2e/suite/command/pull.go b/test/e2e/suite/command/pull.go index db5895da1..2bcb95f36 100644 --- a/test/e2e/suite/command/pull.go +++ b/test/e2e/suite/command/pull.go @@ -91,7 +91,7 @@ var _ = Describe("Remote registry users:", func() { It("should pull specific platform", func() { ORAS("pull", RegistryRef(Host, ImageRepo, "multi"), "--platform", "linux/amd64", "-v", "-o", GinkgoT().TempDir()). - MatchStatus(multi_arch.ImageStateKeys, true, len(multi_arch.ImageStateKeys)).Exec() + MatchStatus(multi_arch.LinuxAMD64StateKeys, true, len(multi_arch.LinuxAMD64StateKeys)).Exec() }) }) })