From d17b0a4168aa209c0e49b6858f503ac2382fb860 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 16 Jan 2023 12:15:43 +0800 Subject: [PATCH 01/46] add test and fix license Signed-off-by: Billy Zha --- .github/workflows/build.yml | 15 +++ test/e2e/internal/utils/exec.go | 2 + test/e2e/internal/utils/file.go | 2 + test/e2e/internal/utils/init.go | 17 +++- test/e2e/internal/utils/match/content.go | 2 + test/e2e/internal/utils/match/keywords.go | 2 + test/e2e/internal/utils/match/matchable.go | 2 + test/e2e/internal/utils/match/status.go | 6 ++ test/e2e/internal/utils/preview.go | 2 + test/e2e/internal/utils/registry.go | 2 + test/e2e/internal/utils/testdata.go | 2 + test/e2e/suite/auth/auth.go | 2 + test/e2e/suite/auth/auth_test.go | 2 + test/e2e/suite/command/blob.go | 2 + test/e2e/suite/command/cp.go | 102 +++++++++++++++++++++ test/e2e/suite/command/manifest.go | 2 + test/e2e/suite/command/pull.go | 2 + test/e2e/suite/command/push.go | 2 + test/e2e/suite/command/repo.go | 2 + test/e2e/suite/command/tag.go | 2 + test/e2e/suite/scenario/oci_image.go | 2 + test/e2e/suite/scenario/scenario_test.go | 2 + 22 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 test/e2e/suite/command/cp.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e25044b38..a41fad12f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,6 +46,8 @@ jobs: cd $GITHUB_WORKSPACE/test/e2e go install github.com/onsi/ginkgo/v2/ginkgo mnt_root="$GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount" + + # Prepare Registry rm -rf $mnt_root/docker for layer in $(ls $mnt_root/*.tar.gz); do tar -xvzf $layer -C $mnt_root @@ -58,11 +60,24 @@ jobs: --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ ghcr.io/oras-project/registry:v1.0.0-rc.3 + + # Prepare Fallback Registry + trap 'docker kill oras-e2e-fallback || true' ERR + docker run --pull always -d -p 6000:5000 --rm --name oras-e2e-fallback \ + --env REGISTRY_STORAGE_DELETE_ENABLED=true \ + --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ + --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ + --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ + registry + + # Test and Cleanup ginkgo -r -p --succinct suite docker kill oras-e2e || true + docker kill oras-e2e-fallback || true env: ORAS_PATH: bin/linux/amd64/oras ORAS_REGISTRY_HOST: localhost:5000 + ORAS_REGISTRY_HOST_FALLBACK: localhost:6000 - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/test/e2e/internal/utils/exec.go b/test/e2e/internal/utils/exec.go index 5e328cf5e..723952d5b 100644 --- a/test/e2e/internal/utils/exec.go +++ b/test/e2e/internal/utils/exec.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/file.go b/test/e2e/internal/utils/file.go index e18473bfe..c5cd1b3ca 100644 --- a/test/e2e/internal/utils/file.go +++ b/test/e2e/internal/utils/file.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/init.go b/test/e2e/internal/utils/init.go index 0c47352de..ef9515a1c 100644 --- a/test/e2e/internal/utils/init.go +++ b/test/e2e/internal/utils/init.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -31,19 +33,32 @@ var ORASPath string // Host points to the registry service where E2E specs will be run against. var Host string +// Host points to the registry service where fallback E2E specs will be run against. +var FallbackHost string + func init() { Host = os.Getenv("ORAS_REGISTRY_HOST") if Host == "" { Host = "localhost:5000" fmt.Fprintln(os.Stderr, "cannot find host name in ORAS_REGISTRY_HOST, using", Host, "instead") } - ref := registry.Reference{ Registry: Host, } if err := ref.ValidateRegistry(); err != nil { panic(err) } + + FallbackHost = os.Getenv("ORAS_REGISTRY_HOST") + if FallbackHost == "" { + FallbackHost = "localhost:6000" + fmt.Fprintln(os.Stderr, "cannot find fallback host name in ORAS_REGISTRY_HOST_FALLBACK, using", FallbackHost, "instead") + } + ref.Registry = FallbackHost + if err := ref.ValidateRegistry(); err != nil { + panic(err) + } + // setup test data pwd, err := os.Getwd() if err != nil { diff --git a/test/e2e/internal/utils/match/content.go b/test/e2e/internal/utils/match/content.go index e70d6cc69..15f84af84 100644 --- a/test/e2e/internal/utils/match/content.go +++ b/test/e2e/internal/utils/match/content.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/match/keywords.go b/test/e2e/internal/utils/match/keywords.go index 6f3831f1f..bdd4e8a0f 100644 --- a/test/e2e/internal/utils/match/keywords.go +++ b/test/e2e/internal/utils/match/keywords.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/match/matchable.go b/test/e2e/internal/utils/match/matchable.go index d25ac587c..4d06dcdaa 100644 --- a/test/e2e/internal/utils/match/matchable.go +++ b/test/e2e/internal/utils/match/matchable.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/match/status.go b/test/e2e/internal/utils/match/status.go index 637d38a64..2204626a0 100644 --- a/test/e2e/internal/utils/match/status.go +++ b/test/e2e/internal/utils/match/status.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -69,6 +71,10 @@ func newStateMachine(cmd string) *stateMachine { sm.addPath("Downloading", "Processing", "Downloaded") sm.addPath("Skipped") sm.addPath("Restored") + case "copy", "cp": + sm.addPath("Copying", "Copied") + sm.addPath("Skipped") + sm.addPath("Exists") default: panic("Unrecognized cmd name " + cmd) } diff --git a/test/e2e/internal/utils/preview.go b/test/e2e/internal/utils/preview.go index 19e0906d5..a7684cf9c 100644 --- a/test/e2e/internal/utils/preview.go +++ b/test/e2e/internal/utils/preview.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/registry.go b/test/e2e/internal/utils/registry.go index 1e1765d5b..c3c0022ca 100644 --- a/test/e2e/internal/utils/registry.go +++ b/test/e2e/internal/utils/registry.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/internal/utils/testdata.go b/test/e2e/internal/utils/testdata.go index 8621065f6..9edea914d 100644 --- a/test/e2e/internal/utils/testdata.go +++ b/test/e2e/internal/utils/testdata.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/auth/auth.go b/test/e2e/suite/auth/auth.go index 04066d259..55ee99fb8 100644 --- a/test/e2e/suite/auth/auth.go +++ b/test/e2e/suite/auth/auth.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/auth/auth_test.go b/test/e2e/suite/auth/auth_test.go index 734d459e8..35ed9169e 100644 --- a/test/e2e/suite/auth/auth_test.go +++ b/test/e2e/suite/auth/auth_test.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/blob.go b/test/e2e/suite/command/blob.go index 9ba7e754a..08f541e51 100644 --- a/test/e2e/suite/command/blob.go +++ b/test/e2e/suite/command/blob.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go new file mode 100644 index 000000000..b282af4e6 --- /dev/null +++ b/test/e2e/suite/command/cp.go @@ -0,0 +1,102 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package command + +import ( + "fmt" + "strings" + + . "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + . "oras.land/oras/test/e2e/internal/utils" + "oras.land/oras/test/e2e/internal/utils/match" +) + +func cpRepo(text string) string { + return fmt.Sprintf("command/copy/%d/%s", GinkgoRandomSeed(), text) +} + +var _ = Describe("ORAS beginners:", func() { + When("running repo command", func() { + RunAndShowPreviewInHelp([]string{"copy"}) + + It("should show preview and help doc", func() { + ORAS("cp", "--help").MatchKeyWords("[Preview] Copy", PreviewDesc, ExampleDesc).Exec() + }) + + It("should fail when no reference provided", func() { + ORAS("cp").ExpectFailure().MatchErrKeyWords("Error:").Exec() + }) + + It("should fail when no destination reference provided", func() { + ORAS("cp", Reference(Host, Repo, FoobarImageTag)).ExpectFailure().MatchErrKeyWords("Error:").Exec() + }) + + It("should fail when source doesn't exist", func() { + ORAS("cp", Reference(Host, Repo, "i-dont-think-this-tag-exists"), Reference(Host, cpRepo("nonexistent-source"), "")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + }) + }) +}) + +var _ = Describe("Common registry users:", Focus, func() { + When("running `cp`", func() { + imageStateKeys := []match.StateKey{ + {Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"}, + {Digest: "fcde2b2edba5", Name: "bar"}, + {Digest: "2c26b46b68ff", Name: "foo1"}, + {Digest: "2c26b46b68ff", Name: "foo2"}, + {Digest: "fd6ed2f36b54", Name: "application/vnd.oci.image.manifest.v1+json"}, + } + validate := func(src, dst string) { + srcManifest := ORAS("manifest", "fetch", src).Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).Exec().Out.Contents() + gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) + } + + It("should copy an image to a new repository via tag", func() { + src := Reference(Host, Repo, FoobarImageTag) + dst := Reference(Host, cpRepo("copy-tag"), "copiedTag") + ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + validate(src, dst) + }) + + It("should copy an image to a new repository via tag without tagging", func() { + src := Reference(Host, Repo, FoobarImageTag) + dst := Reference(Host, cpRepo("copy-no-tagging"), FoobarImageDigest) + ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + validate(src, dst) + }) + + It("should copy an image to a new repository via digest", func() { + src := Reference(Host, Repo, FoobarImageDigest) + dst := Reference(Host, cpRepo("copy-digest"), "copiedTag") + ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + validate(src, dst) + }) + + It("should copy an image to a new repository with multiple tagging", func() { + src := Reference(Host, Repo, FoobarImageDigest) + tags := []string{"tag1", "tag2", "tag3"} + dstRepo := cpRepo("copy-multi-tagging") + dst := Reference(Host, dstRepo, "") + ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + for _, tag := range tags { + dst := Reference(Host, dstRepo, tag) + validate(src, dst) + } + }) + }) +}) diff --git a/test/e2e/suite/command/manifest.go b/test/e2e/suite/command/manifest.go index d1c445031..df5a9de45 100644 --- a/test/e2e/suite/command/manifest.go +++ b/test/e2e/suite/command/manifest.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/pull.go b/test/e2e/suite/command/pull.go index d62c2c906..353c5cbca 100644 --- a/test/e2e/suite/command/pull.go +++ b/test/e2e/suite/command/pull.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/push.go b/test/e2e/suite/command/push.go index d520da2de..dfb2843e7 100644 --- a/test/e2e/suite/command/push.go +++ b/test/e2e/suite/command/push.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/repo.go b/test/e2e/suite/command/repo.go index 21fd91536..eb037835c 100644 --- a/test/e2e/suite/command/repo.go +++ b/test/e2e/suite/command/repo.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/command/tag.go b/test/e2e/suite/command/tag.go index 2c0de569e..422cb05b8 100644 --- a/test/e2e/suite/command/tag.go +++ b/test/e2e/suite/command/tag.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/scenario/oci_image.go b/test/e2e/suite/scenario/oci_image.go index 5bc72421b..f34199b0f 100644 --- a/test/e2e/suite/scenario/oci_image.go +++ b/test/e2e/suite/scenario/oci_image.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/test/e2e/suite/scenario/scenario_test.go b/test/e2e/suite/scenario/scenario_test.go index b3814a6b6..f58e393ae 100644 --- a/test/e2e/suite/scenario/scenario_test.go +++ b/test/e2e/suite/scenario/scenario_test.go @@ -3,7 +3,9 @@ Copyright The ORAS Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From f4e7398ec9f24bdb9529532d9315bd7ab3ec3349 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 16 Jan 2023 12:22:44 +0800 Subject: [PATCH 02/46] Remove focused 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 b282af4e6..b05fbf95c 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -51,7 +51,7 @@ var _ = Describe("ORAS beginners:", func() { }) }) -var _ = Describe("Common registry users:", Focus, func() { +var _ = Describe("Common registry users:", func() { When("running `cp`", func() { imageStateKeys := []match.StateKey{ {Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"}, From ee2436d18c0fbe373125fed4a9c3927074dfd2b4 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 16 Jan 2023 16:32:58 +0800 Subject: [PATCH 03/46] add fallback Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- test/e2e/internal/utils/init.go | 3 + test/e2e/internal/utils/testdata.go | 4 +- test/e2e/suite/command/blob.go | 30 +++--- test/e2e/suite/command/cp.go | 97 +++++++++++++----- test/e2e/suite/command/manifest.go | 94 ++++++++--------- test/e2e/suite/command/repo.go | 16 +-- test/e2e/suite/command/tag.go | 10 +- .../distribution/mount/artifacts.tar.gz | Bin 0 -> 2294 bytes 9 files changed, 156 insertions(+), 100 deletions(-) create mode 100644 test/e2e/testdata/distribution/mount/artifacts.tar.gz diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a41fad12f..2c3030ed2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: # Prepare Registry rm -rf $mnt_root/docker - for layer in $(ls $mnt_root/*.tar.gz); do + for layer in $(ls -rt $mnt_root/*.tar.gz); do tar -xvzf $layer -C $mnt_root done trap 'docker kill oras-e2e || true' ERR diff --git a/test/e2e/internal/utils/init.go b/test/e2e/internal/utils/init.go index ef9515a1c..1006226bc 100644 --- a/test/e2e/internal/utils/init.go +++ b/test/e2e/internal/utils/init.go @@ -83,7 +83,10 @@ func init() { fmt.Printf("Testing based on temp binary locates in %q\n", ORASPath) } + // Login cmd := exec.Command(ORASPath, "login", Host, "-u", Username, "-p", Password) gomega.Expect(cmd.Run()).ShouldNot(gomega.HaveOccurred()) + cmd = exec.Command(ORASPath, "login", FallbackHost, "-u", Username, "-p", Password) + gomega.Expect(cmd.Run()).ShouldNot(gomega.HaveOccurred()) }) } diff --git a/test/e2e/internal/utils/testdata.go b/test/e2e/internal/utils/testdata.go index 9edea914d..e62a561d6 100644 --- a/test/e2e/internal/utils/testdata.go +++ b/test/e2e/internal/utils/testdata.go @@ -18,12 +18,14 @@ package utils const ( PreviewDesc = "** This command is in preview and under development. **" ExampleDesc = "\nExample - " - Repo = "command/images" + ImageRepo = "command/images" + ArtifactRepo = "command/artifacts" FoobarImageTag = "foobar" FoobarConfigDesc = "{\"mediaType\":\"application/vnd.unknown.config.v1+json\",\"digest\":\"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a\",\"size\":2}" MultiImageTag = "multi" MultiImageDigest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" FoobarImageDigest = "sha256:fd6ed2f36b5465244d5dc86cb4e7df0ab8a9d24adc57825099f522fe009a22bb" + FallbackSbomArtifactDigest = "sha256:611c758459382363aaef8862e5884f5887d1f823f257457a508501d46ebbfbdf" MultiImageManifest = `{"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"}}]}` MultiImageDescriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` LinuxAMD64ImageManifest = `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:2ef548696ac7dd66ef38aab5cc8fc5cc1fb637dfaedb3a9afc89bf16db9277e1","size":10240,"annotations":{"org.opencontainers.image.title":"hello.tar"}}]}` diff --git a/test/e2e/suite/command/blob.go b/test/e2e/suite/command/blob.go index 08f541e51..86f50345a 100644 --- a/test/e2e/suite/command/blob.go +++ b/test/e2e/suite/command/blob.go @@ -106,22 +106,22 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail if neither output path nor descriptor flag are not provided", func() { - ORAS("blob", "fetch", Reference(Host, Repo, "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae")). + ORAS("blob", "fetch", Reference(Host, ImageRepo, "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae")). ExpectFailure().Exec() }) It("should fail if no digest provided", func() { - ORAS("blob", "fetch", Reference(Host, Repo, "")). + ORAS("blob", "fetch", Reference(Host, ImageRepo, "")). ExpectFailure().Exec() }) It("should fail if provided digest doesn't existed", func() { - ORAS("blob", "fetch", Reference(Host, Repo, "sha256:2aaa2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a")). + ORAS("blob", "fetch", Reference(Host, ImageRepo, "sha256:2aaa2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a")). ExpectFailure().Exec() }) It("should fail if output path points to stdout and descriptor flag is provided", func() { - ORAS("blob", "fetch", Reference(Host, Repo, ""), "--descriptor", "--output", "-"). + ORAS("blob", "fetch", Reference(Host, ImageRepo, ""), "--descriptor", "--output", "-"). ExpectFailure().Exec() }) @@ -136,14 +136,14 @@ var _ = Describe("ORAS beginners:", func() { It("should fail if no blob reference is provided", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "no-ref") - ORAS("cp", Reference(Host, Repo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() + ORAS("cp", Reference(Host, ImageRepo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() ORAS("blob", "delete").ExpectFailure().Exec() ORAS("blob", "fetch", Reference(Host, dstRepo, deleteDigest), "--output", "-").MatchContent(deleteContent).Exec() }) It("should fail if no force flag and descriptor flag is provided", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "no-confirm") - ORAS("cp", Reference(Host, Repo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() + ORAS("cp", Reference(Host, ImageRepo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() ORAS("blob", "delete", Reference(Host, dstRepo, deleteDigest), "--descriptor").ExpectFailure().Exec() ORAS("blob", "fetch", Reference(Host, dstRepo, deleteDigest), "--output", "-").MatchContent(deleteContent).Exec() }) @@ -156,7 +156,7 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail to delete a non-existent blob without force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("blob", "delete", toDeleteRef). ExpectFailure(). MatchErrKeyWords(toDeleteRef, "the specified blob does not exist"). @@ -164,7 +164,7 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail to delete a non-existent blob and output descriptor, with force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("blob", "delete", toDeleteRef, "--force", "--descriptor"). ExpectFailure(). MatchErrKeyWords(toDeleteRef, "the specified blob does not exist"). @@ -178,7 +178,7 @@ var _ = Describe("Common registry users:", func() { When("running `blob delete`", func() { It("should delete a blob with interactive confirmation", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "prompt-confirmation") - ORAS("cp", Reference(Host, Repo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() + ORAS("cp", Reference(Host, ImageRepo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() toDeleteRef := Reference(Host, dstRepo, deleteDigest) ORAS("blob", "delete", toDeleteRef). WithInput(strings.NewReader("y")). @@ -192,14 +192,14 @@ var _ = Describe("Common registry users:", func() { It("should delete a blob with force flag and output descriptor", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "flag-confirmation") - ORAS("cp", Reference(Host, Repo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() + ORAS("cp", Reference(Host, ImageRepo, FoobarImageDigest), Reference(Host, dstRepo, FoobarImageDigest)).Exec() toDeleteRef := Reference(Host, dstRepo, deleteDigest) ORAS("blob", "delete", toDeleteRef, "--force", "--descriptor").MatchContent(deleteDescriptor).Exec() ORAS("blob", "delete", toDeleteRef).WithDescription("validate").ExpectFailure().MatchErrKeyWords("Error:", toDeleteRef, "the specified blob does not exist").Exec() }) It("should return success when deleting a non-existent blob with force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("blob", "delete", toDeleteRef, "--force"). MatchKeyWords("Missing", toDeleteRef). Exec() @@ -235,24 +235,24 @@ var _ = Describe("Common registry users:", func() { var blobDescriptor = `{"mediaType":"application/octet-stream","digest":"sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae","size":3}` When("running `blob fetch`", func() { It("should fetch blob descriptor ", func() { - ORAS("blob", "fetch", Reference(Host, Repo, blobDigest), "--descriptor"). + ORAS("blob", "fetch", Reference(Host, ImageRepo, blobDigest), "--descriptor"). MatchContent(blobDescriptor).Exec() }) It("should fetch blob content and output to stdout", func() { - ORAS("blob", "fetch", Reference(Host, Repo, blobDigest), "--output", "-"). + ORAS("blob", "fetch", Reference(Host, ImageRepo, blobDigest), "--output", "-"). MatchContent(blobContent).Exec() }) It("should fetch blob content and output to a file", func() { tempDir := GinkgoT().TempDir() contentPath := filepath.Join(tempDir, "fetched") - ORAS("blob", "fetch", Reference(Host, Repo, blobDigest), "--output", contentPath). + ORAS("blob", "fetch", Reference(Host, ImageRepo, blobDigest), "--output", contentPath). WithWorkDir(tempDir).Exec() MatchFile(contentPath, blobContent, DefaultTimeout) }) It("should fetch blob descriptor and output content to a file", func() { tempDir := GinkgoT().TempDir() contentPath := filepath.Join(tempDir, "fetched") - ORAS("blob", "fetch", Reference(Host, Repo, blobDigest), "--output", contentPath, "--descriptor"). + ORAS("blob", "fetch", Reference(Host, ImageRepo, blobDigest), "--output", contentPath, "--descriptor"). MatchContent(blobDescriptor). WithWorkDir(tempDir).Exec() MatchFile(contentPath, blobContent, DefaultTimeout) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index b05fbf95c..5488ec172 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -25,7 +25,7 @@ import ( "oras.land/oras/test/e2e/internal/utils/match" ) -func cpRepo(text string) string { +func cpTestRepo(text string) string { return fmt.Sprintf("command/copy/%d/%s", GinkgoRandomSeed(), text) } @@ -42,61 +42,112 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail when no destination reference provided", func() { - ORAS("cp", Reference(Host, Repo, FoobarImageTag)).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("cp", Reference(Host, ImageRepo, FoobarImageTag)).ExpectFailure().MatchErrKeyWords("Error:").Exec() }) It("should fail when source doesn't exist", func() { - ORAS("cp", Reference(Host, Repo, "i-dont-think-this-tag-exists"), Reference(Host, cpRepo("nonexistent-source"), "")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("cp", Reference(Host, ImageRepo, "i-dont-think-this-tag-exists"), Reference(Host, cpTestRepo("nonexistent-source"), "")).ExpectFailure().MatchErrKeyWords("Error:").Exec() }) }) }) +var ( + foobarImageStates = []match.StateKey{ + {Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"}, + {Digest: "fcde2b2edba5", Name: "bar"}, + {Digest: "2c26b46b68ff", Name: "foo1"}, + {Digest: "2c26b46b68ff", Name: "foo2"}, + {Digest: "fd6ed2f36b54", Name: "application/vnd.oci.image.manifest.v1+json"}, + } + foobarReferrersStates = []match.StateKey{ + {Digest: "8d7a27ff2662", Name: "application/vnd.oci.artifact.manifest.v1+json"}, + {Digest: "2dbea575a349", Name: "application/vnd.oci.artifact.manifest.v1+json"}, + } + foobarFallbackReferrersStates = []match.StateKey{ + {Digest: "611c75845938", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "4079617a0647", Name: "application/vnd.oci.image.manifest.v1+json"}, + } +) + var _ = Describe("Common registry users:", func() { When("running `cp`", func() { - imageStateKeys := []match.StateKey{ - {Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"}, - {Digest: "fcde2b2edba5", Name: "bar"}, - {Digest: "2c26b46b68ff", Name: "foo1"}, - {Digest: "2c26b46b68ff", Name: "foo2"}, - {Digest: "fd6ed2f36b54", Name: "application/vnd.oci.image.manifest.v1+json"}, - } validate := func(src, dst string) { srcManifest := ORAS("manifest", "fetch", src).Exec().Out.Contents() dstManifest := ORAS("manifest", "fetch", dst).Exec().Out.Contents() gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) } - It("should copy an image to a new repository via tag", func() { - src := Reference(Host, Repo, FoobarImageTag) - dst := Reference(Host, cpRepo("copy-tag"), "copiedTag") - ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + src := Reference(Host, ImageRepo, FoobarImageTag) + dst := Reference(Host, cpTestRepo("copy-tag"), "copiedTag") + ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() validate(src, dst) }) It("should copy an image to a new repository via tag without tagging", func() { - src := Reference(Host, Repo, FoobarImageTag) - dst := Reference(Host, cpRepo("copy-no-tagging"), FoobarImageDigest) - ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + src := Reference(Host, ImageRepo, FoobarImageTag) + dst := Reference(Host, cpTestRepo("copy-no-tagging"), FoobarImageDigest) + ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + validate(src, dst) + }) + + It("should copy an image and its referrers to a new repository", func() { + stateKeys := append(append(foobarImageStates, foobarReferrersStates...), foobarFallbackReferrersStates...) + src := Reference(Host, ArtifactRepo, FoobarImageTag) + dst := Reference(Host, cpTestRepo("copy-no-tagging"), FoobarImageDigest) + ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() validate(src, dst) }) It("should copy an image to a new repository via digest", func() { - src := Reference(Host, Repo, FoobarImageDigest) - dst := Reference(Host, cpRepo("copy-digest"), "copiedTag") - ORAS("cp", src, dst, "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + src := Reference(Host, ImageRepo, FoobarImageDigest) + dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") + ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() validate(src, dst) }) + It("should copy an certain platform of image to a new repository via tag", func() { + src := Reference(Host, ImageRepo, MultiImageTag) + dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) + }) + + It("should copy an certain platform of image to a new repository via digest", func() { + src := Reference(Host, ImageRepo, MultiImageDigest) + dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) + }) + It("should copy an image to a new repository with multiple tagging", func() { - src := Reference(Host, Repo, FoobarImageDigest) + src := Reference(Host, ImageRepo, FoobarImageDigest) tags := []string{"tag1", "tag2", "tag3"} - dstRepo := cpRepo("copy-multi-tagging") + dstRepo := cpTestRepo("copy-multi-tagging") dst := Reference(Host, dstRepo, "") - ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(imageStateKeys, true, len(imageStateKeys)).Exec() + ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() for _, tag := range tags { dst := Reference(Host, dstRepo, tag) validate(src, dst) } }) + + }) +}) + +var _ = Describe("OCI spec 1.0 registry users:", func() { + When("running `cp`", func() { + validate := func(src, dst string) { + srcManifest := ORAS("manifest", "fetch", src).Exec().Out.Contents() + dstManifest := ORAS("manifest", "fetch", dst).Exec().Out.Contents() + gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) + } + It("should copy an image artifact and its referrers to a fallback repository", func() { + stateKeys := append(foobarImageStates, foobarFallbackReferrersStates...) + src := Reference(Host, ArtifactRepo, FallbackSbomArtifactDigest) + dst := Reference(FallbackHost, cpTestRepo("copy-fallback"), "") + ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() + validate(src, dst) + }) + }) }) diff --git a/test/e2e/suite/command/manifest.go b/test/e2e/suite/command/manifest.go index df5a9de45..e6a0f9bc1 100644 --- a/test/e2e/suite/command/manifest.go +++ b/test/e2e/suite/command/manifest.go @@ -79,7 +79,7 @@ var _ = Describe("ORAS beginners:", func() { tempTag := "to-delete" It("should cancel deletion without confirmation", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "no-confirm") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete", Reference(Host, dstRepo, tempTag)). MatchKeyWords("Operation cancelled.", "Are you sure you want to delete the manifest ", " and all tags associated with it?").Exec() validate(Reference(Host, dstRepo, ""), tempTag, false) @@ -87,12 +87,12 @@ var _ = Describe("ORAS beginners:", func() { It("should fail if descriptor flag is provided without confirmation flag", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "descriptor-without-confirm") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete", Reference(Host, dstRepo, tempTag), "--descriptor").ExpectFailure().Exec() }) It("should fail to delete a non-existent manifest via digest without force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("manifest", "delete", toDeleteRef). ExpectFailure(). MatchErrKeyWords(toDeleteRef, "the specified manifest does not exist"). @@ -100,7 +100,7 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail to delete a non-existent manifest and output descriptor via digest, with force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("manifest", "delete", toDeleteRef, "--force", "--descriptor"). ExpectFailure(). MatchErrKeyWords(toDeleteRef, "the specified manifest does not exist"). @@ -108,7 +108,7 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail to delete a non-existent manifest and output descriptor via tag, without force flag set", func() { - toDeleteRef := Reference(Host, Repo, "this.tag.should-not.be-existed") + toDeleteRef := Reference(Host, ImageRepo, "this.tag.should-not.be-existed") ORAS("manifest", "delete", toDeleteRef, "--force", "--descriptor"). ExpectFailure(). MatchErrKeyWords(toDeleteRef, "the specified manifest does not exist"). @@ -117,7 +117,7 @@ var _ = Describe("ORAS beginners:", func() { It("should fail if no blob reference provided", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "no-reference") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete").ExpectFailure().Exec() }) }) @@ -132,10 +132,10 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail if provided reference does not exist", func() { - ORAS("manifest", "fetch-config", Reference(Host, Repo, "this-tag-should-not-exist")).ExpectFailure().Exec() + ORAS("manifest", "fetch-config", Reference(Host, ImageRepo, "this-tag-should-not-exist")).ExpectFailure().Exec() }) It("should fail fetching a config of non-image manifest type", func() { - ORAS("manifest", "fetch-config", Reference(Host, Repo, MultiImageTag)).ExpectFailure().Exec() + ORAS("manifest", "fetch-config", Reference(Host, ImageRepo, MultiImageTag)).ExpectFailure().Exec() }) }) }) @@ -145,103 +145,103 @@ var _ = Describe("Common registry users:", func() { repoFmt := fmt.Sprintf("command/manifest/%%s/%d/%%s", GinkgoRandomSeed()) When("running `manifest fetch`", func() { It("should fetch manifest list with digest", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag)). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag)). MatchContent(MultiImageManifest).Exec() }) It("should fetch manifest list with tag", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag)). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag)). MatchContent(MultiImageManifest).Exec() }) It("should fetch manifest list to stdout", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag), "--output", "-"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag), "--output", "-"). MatchContent(MultiImageManifest).Exec() }) It("should fetch manifest to file and output descriptor to stdout", func() { fetchPath := filepath.Join(GinkgoT().TempDir(), "fetchedImage") - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag), "--output", fetchPath, "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag), "--output", fetchPath, "--descriptor"). MatchContent(MultiImageDescriptor).Exec() MatchFile(fetchPath, MultiImageManifest, DefaultTimeout) }) It("should fetch manifest via tag with platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag), "--platform", "linux/amd64"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag), "--platform", "linux/amd64"). MatchContent(LinuxAMD64ImageManifest).Exec() }) It("should fetch manifest via digest with platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--platform", "linux/amd64"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--platform", "linux/amd64"). MatchContent(LinuxAMD64ImageManifest).Exec() }) It("should fetch manifest with platform validation", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, LinuxAMD64ImageDigest), "--platform", "linux/amd64"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, LinuxAMD64ImageDigest), "--platform", "linux/amd64"). MatchContent(LinuxAMD64ImageManifest).Exec() }) It("should fetch descriptor via digest", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--descriptor"). MatchContent(MultiImageDescriptor).Exec() }) It("should fetch descriptor via digest with platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--platform", "linux/amd64", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--platform", "linux/amd64", "--descriptor"). MatchContent(LinuxAMD64ImageIndexDescriptor).Exec() }) It("should fetch descriptor via digest with platform validation", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--descriptor"). MatchContent(LinuxAMD64ImageDescriptor).Exec() }) It("should fetch descriptor via tag", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--descriptor"). MatchContent(MultiImageDescriptor).Exec() }) It("should fetch descriptor via tag with platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--platform", "linux/amd64", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--platform", "linux/amd64", "--descriptor"). MatchContent(LinuxAMD64ImageIndexDescriptor).Exec() }) It("should fetch index content with media type assertion", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--media-type", "application/vnd.oci.image.index.v1+json"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--media-type", "application/vnd.oci.image.index.v1+json"). MatchContent(MultiImageManifest).Exec() }) It("should fetch index descriptor with media type assertion", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--media-type", "application/vnd.oci.image.index.v1+json", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--media-type", "application/vnd.oci.image.index.v1+json", "--descriptor"). MatchContent(MultiImageDescriptor).Exec() }) It("should fetch image content with media type assertion and platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json"). MatchContent(LinuxAMD64ImageManifest).Exec() - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). MatchContent(LinuxAMD64ImageIndexDescriptor).Exec() }) It("should fetch image descriptor with media type assertion and platform selection", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageTag), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageTag), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). MatchContent(LinuxAMD64ImageIndexDescriptor).Exec() - ORAS("manifest", "fetch", Reference(Host, Repo, MultiImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, MultiImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.index.v1+json,application/vnd.oci.image.manifest.v1+json", "--descriptor"). MatchContent(LinuxAMD64ImageIndexDescriptor).Exec() }) It("should fetch image content with media type assertion and platform validation", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.manifest.v1+json"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.manifest.v1+json"). MatchContent(LinuxAMD64ImageManifest).Exec() }) It("should fetch image descriptor with media type assertion and platform validation", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.manifest.v1+json", "--descriptor"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, LinuxAMD64ImageDigest), "--platform", "linux/amd64", "--media-type", "application/vnd.oci.image.manifest.v1+json", "--descriptor"). MatchContent(LinuxAMD64ImageDescriptor).Exec() }) It("should fail to fetch image if media type assertion fails", func() { - ORAS("manifest", "fetch", Reference(Host, Repo, LinuxAMD64ImageDigest), "--media-type", "this.will.not.be.found"). + ORAS("manifest", "fetch", Reference(Host, ImageRepo, LinuxAMD64ImageDigest), "--media-type", "this.will.not.be.found"). ExpectFailure(). MatchErrKeyWords(LinuxAMD64ImageDigest, "error: ", "not found").Exec() }) @@ -254,14 +254,14 @@ var _ = Describe("Common registry users:", func() { It("should push a manifest from stdin without media type flag", func() { tag := "from-stdin" - ORAS("manifest", "push", Reference(Host, Repo, tag), "-"). - MatchKeyWords("Pushed", Reference(Host, Repo, tag), "Digest:", digest). + ORAS("manifest", "push", Reference(Host, ImageRepo, tag), "-"). + MatchKeyWords("Pushed", Reference(Host, ImageRepo, tag), "Digest:", digest). WithInput(strings.NewReader(manifest)).Exec() }) It("should push a manifest and output descriptor", func() { tag := "from-stdin" - ORAS("manifest", "push", Reference(Host, Repo, tag), "-", "--descriptor"). + ORAS("manifest", "push", Reference(Host, ImageRepo, tag), "-", "--descriptor"). MatchContent(descriptor). WithInput(strings.NewReader(manifest)).Exec() }) @@ -269,8 +269,8 @@ var _ = Describe("Common registry users:", func() { It("should push a manifest from file", func() { manifestPath := WriteTempFile("manifest.json", manifest) tag := "from-file" - ORAS("manifest", "push", Reference(Host, Repo, tag), manifestPath, "--media-type", ocispec.MediaTypeImageManifest). - MatchKeyWords("Pushed", Reference(Host, Repo, tag), "Digest:", digest). + ORAS("manifest", "push", Reference(Host, ImageRepo, tag), manifestPath, "--media-type", ocispec.MediaTypeImageManifest). + MatchKeyWords("Pushed", Reference(Host, ImageRepo, tag), "Digest:", digest). WithInput(strings.NewReader(manifest)).Exec() }) @@ -278,11 +278,11 @@ var _ = Describe("Common registry users:", func() { manifest := `{"schemaVersion":2,"config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53}}` digest := "sha256:0c2ae2c73c5dde0a42582d328b2e2ea43f36ba20f604fa8706f441ac8b0a3445" tag := "mediatype-flag" - ORAS("manifest", "push", Reference(Host, Repo, tag), "-", "--media-type", ocispec.MediaTypeImageManifest). - MatchKeyWords("Pushed", Reference(Host, Repo, tag), "Digest:", digest). + ORAS("manifest", "push", Reference(Host, ImageRepo, tag), "-", "--media-type", ocispec.MediaTypeImageManifest). + MatchKeyWords("Pushed", Reference(Host, ImageRepo, tag), "Digest:", digest). WithInput(strings.NewReader(manifest)).Exec() - ORAS("manifest", "push", Reference(Host, Repo, ""), "-"). + ORAS("manifest", "push", Reference(Host, ImageRepo, ""), "-"). WithInput(strings.NewReader(manifest)). ExpectFailure(). WithDescription("fail if no media type flag provided").Exec() @@ -291,32 +291,32 @@ var _ = Describe("Common registry users:", func() { When("running `manifest fetch-config`", func() { It("should fetch a config via a tag", func() { - ORAS("manifest", "fetch-config", Reference(Host, Repo, FoobarImageTag)). + ORAS("manifest", "fetch-config", Reference(Host, ImageRepo, FoobarImageTag)). MatchContent("{}").Exec() }) It("should fetch a config descriptor via a tag", func() { - ORAS("manifest", "fetch-config", "--descriptor", Reference(Host, Repo, FoobarImageTag)). + ORAS("manifest", "fetch-config", "--descriptor", Reference(Host, ImageRepo, FoobarImageTag)). MatchContent(FoobarConfigDesc).Exec() }) It("should fetch a config via digest", func() { - ORAS("manifest", "fetch-config", Reference(Host, Repo, FoobarImageTag)). + ORAS("manifest", "fetch-config", Reference(Host, ImageRepo, FoobarImageTag)). MatchContent("{}").Exec() }) It("should fetch a config descriptor via a digest", func() { - ORAS("manifest", "fetch-config", "--descriptor", Reference(Host, Repo, FoobarImageDigest)). + ORAS("manifest", "fetch-config", "--descriptor", Reference(Host, ImageRepo, FoobarImageDigest)). MatchContent(FoobarConfigDesc).Exec() }) It("should fetch a config of a specific platform", func() { - ORAS("manifest", "fetch-config", "--platform", "linux/amd64", Reference(Host, Repo, MultiImageTag)). + ORAS("manifest", "fetch-config", "--platform", "linux/amd64", Reference(Host, ImageRepo, MultiImageTag)). MatchContent(LinuxAMD64ImageConfig).Exec() }) It("should fetch a config descriptor of a specific platform", func() { - ORAS("manifest", "fetch-config", "--descriptor", "--platform", "linux/amd64", Reference(Host, Repo, MultiImageTag)). + ORAS("manifest", "fetch-config", "--descriptor", "--platform", "linux/amd64", Reference(Host, ImageRepo, MultiImageTag)). MatchContent(LinuxAMD64ImageConfigDescriptor).Exec() }) }) @@ -325,7 +325,7 @@ var _ = Describe("Common registry users:", func() { tempTag := "to-delete" It("should do confirmed deletion via input", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "confirm-input") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete", Reference(Host, dstRepo, tempTag)). WithInput(strings.NewReader("y")).Exec() validate(Reference(Host, dstRepo, ""), tempTag, true) @@ -333,14 +333,14 @@ var _ = Describe("Common registry users:", func() { It("should do confirmed deletion via flag", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "confirm-flag") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete", Reference(Host, dstRepo, tempTag), "-f").Exec() validate(Reference(Host, dstRepo, ""), tempTag, true) }) It("should do confirmed deletion and output descriptor", func() { dstRepo := fmt.Sprintf(repoFmt, "delete", "output-descriptor") - prepare(Reference(Host, Repo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) + prepare(Reference(Host, ImageRepo, FoobarImageTag), Reference(Host, dstRepo, tempTag)) ORAS("manifest", "delete", Reference(Host, dstRepo, tempTag), "-f", "--descriptor"). MatchContent("{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:fd6ed2f36b5465244d5dc86cb4e7df0ab8a9d24adc57825099f522fe009a22bb\",\"size\":851}"). WithDescription("cancel without confirmation").Exec() @@ -348,7 +348,7 @@ var _ = Describe("Common registry users:", func() { }) It("should succeed when deleting a non-existent manifest with force flag set", func() { - toDeleteRef := Reference(Host, Repo, invalidDigest) + toDeleteRef := Reference(Host, ImageRepo, invalidDigest) ORAS("manifest", "delete", toDeleteRef, "--force"). MatchKeyWords("Missing", toDeleteRef). Exec() diff --git a/test/e2e/suite/command/repo.go b/test/e2e/suite/command/repo.go index eb037835c..b52c3a973 100644 --- a/test/e2e/suite/command/repo.go +++ b/test/e2e/suite/command/repo.go @@ -36,8 +36,8 @@ var _ = Describe("ORAS beginners:", func() { It("should fail listing repositories if wrong registry provided", func() { ORAS("repo", "ls").ExpectFailure().MatchErrKeyWords("Error:").Exec() - ORAS("repo", "ls", Reference(Host, Repo, "")).ExpectFailure().MatchErrKeyWords("Error:").Exec() - ORAS("repo", "ls", Reference(Host, Repo, "some-tag")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("repo", "ls", Reference(Host, ImageRepo, "")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("repo", "ls", Reference(Host, ImageRepo, "some-tag")).ExpectFailure().MatchErrKeyWords("Error:").Exec() }) }) When("running `repo tags`", func() { @@ -52,7 +52,7 @@ var _ = Describe("ORAS beginners:", func() { It("should fail listing repositories if wrong registry provided", func() { ORAS("repo", "tags").ExpectFailure().MatchErrKeyWords("Error:").Exec() ORAS("repo", "tags", Host).ExpectFailure().MatchErrKeyWords("Error:").Exec() - ORAS("repo", "tags", Reference(Host, Repo, "some-tag")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("repo", "tags", Reference(Host, ImageRepo, "some-tag")).ExpectFailure().MatchErrKeyWords("Error:").Exec() }) }) }) @@ -61,18 +61,18 @@ var _ = Describe("ORAS beginners:", func() { var _ = Describe("Common registry users:", func() { When("running `repo ls`", func() { It("should list repositories", func() { - ORAS("repository", "list", Host).MatchKeyWords(Repo).Exec() + ORAS("repository", "list", Host).MatchKeyWords(ImageRepo).Exec() }) It("should list repositories via short command", func() { - ORAS("repo", "ls", Host).MatchKeyWords(Repo).Exec() + ORAS("repo", "ls", Host).MatchKeyWords(ImageRepo).Exec() }) It("should list partial repositories via `last` flag", func() { - session := ORAS("repo", "ls", Host, "--last", Repo).Exec() - Expect(session.Out).ShouldNot(gbytes.Say(Repo)) + session := ORAS("repo", "ls", Host, "--last", ImageRepo).Exec() + Expect(session.Out).ShouldNot(gbytes.Say(ImageRepo)) }) }) When("running `repo tags`", func() { - repoRef := Reference(Host, Repo, "") + repoRef := Reference(Host, ImageRepo, "") It("should list tags", func() { ORAS("repository", "show-tags", repoRef).MatchKeyWords(MultiImageTag, FoobarImageTag).Exec() }) diff --git a/test/e2e/suite/command/tag.go b/test/e2e/suite/command/tag.go index 422cb05b8..87a3bf829 100644 --- a/test/e2e/suite/command/tag.go +++ b/test/e2e/suite/command/tag.go @@ -28,7 +28,7 @@ var _ = Describe("ORAS beginners:", func() { }) It("should fail when provided manifest reference is not found", func() { - ORAS("tag", Reference(Host, Repo, "i-dont-think-this-tag-exists")).ExpectFailure().MatchErrKeyWords("Error:").Exec() + ORAS("tag", Reference(Host, ImageRepo, "i-dont-think-this-tag-exists")).ExpectFailure().MatchErrKeyWords("Error:").Exec() }) }) }) @@ -40,16 +40,16 @@ var _ = Describe("Common registry users:", func() { } When("running `tag`", func() { It("should add a tag to an existent manifest when providing tag reference", func() { - tagAndValidate(Host, Repo, MultiImageTag, "tag-via-tag") + tagAndValidate(Host, ImageRepo, MultiImageTag, "tag-via-tag") }) It("should add a tag to an existent manifest when providing digest reference", func() { - tagAndValidate(Host, Repo, MultiImageDigest, "tag-via-digest") + tagAndValidate(Host, ImageRepo, MultiImageDigest, "tag-via-digest") }) It("should add multiple tags to an existent manifest when providing digest reference", func() { - tagAndValidate(Host, Repo, MultiImageDigest, "tag1-via-digest", "tag2-via-digest", "tag3-via-digest") + tagAndValidate(Host, ImageRepo, MultiImageDigest, "tag1-via-digest", "tag2-via-digest", "tag3-via-digest") }) It("should add multiple tags to an existent manifest when providing tag reference", func() { - tagAndValidate(Host, Repo, MultiImageTag, "tag1-via-tag", "tag1-via-tag", "tag1-via-tag") + tagAndValidate(Host, ImageRepo, MultiImageTag, "tag1-via-tag", "tag1-via-tag", "tag1-via-tag") }) }) }) diff --git a/test/e2e/testdata/distribution/mount/artifacts.tar.gz b/test/e2e/testdata/distribution/mount/artifacts.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..aee352e7e6557be856c08c343674f4875b3d1cc2 GIT binary patch literal 2294 zcmZuxc|6qn8YasK8J%=!VU%d3Y!wp5lCrPG)xnfDk~MWi{EFm+tc5I1%97;bBvCY> zp^|KsP$6L~(_;M0Vt&8(j?TUR-S5Be=li_x^SsaVl9HuFemyZ(;ZXRxhSk=X=9VT6 zHBrK!rkwM5^a(eSti5Y|fp%JBr1ggJ^a0aw`oYEVfq}$vzna*Y$8 z(JrbRxlwhZ0-ft{-O>_@HS6}05_0&+<*ABip_qh}6aPrH6@)&Js?ZE<4#i4Tp?y>b zrD8+zy;=L?P0{{>zzrq$_*xrwPNg+R^R3oWB_x@|$7_*yRC_Do-$Xp!a^V$Or}#+A z-P^fwzlFl^C@=$KzD7G?lr zHuNKE)!B18=wuS&sdy6aLq})FGX6ce+sPYAS)d69A8Bu{$(Iw*wl}pxAkYpsif;al zg9*JeU;XI4nX#xUy6Cul$KOB?dCgKw{WPZymEIO{{L zGaD<~>&uh14QO?YuzZnudy8ghU{I*#&!b*F*0#so=e3uU!h7rra(GUrH=`(7Z2!=j6sp8MTQ^kr`E zd0FToA1AlVCVQLVEB|cE>bweZM)>%ddFe~ZsXy<=S3_E?-~!XLW2X^n+W1^0qiR#( z%mEqYYxl=9^~)71Q!S2XNwb^Z?~v*3uMgZxp73@RQQtmc^7heagTKemfQ_^WPdE3k zi!aZ0K}@hdoVYOY&#Yp>gx9d9)CN%?Lt!1R#G=hn=@Fw)-3ppe6#ArxQfS&0`0%O9 zvK-DJ;(B!g@y8UUqnVVmZ^tk2NZV32VtTi52$)Ih!OLM-$A?Qnc7Z!a;{(|Qh1M^1 zXo>ua4?TDeFSsVJQnAD9=J9nSH!*t10t7iE@DZiPGQ|7iXFjjq`hplAEKjjdJZa^i7WAmPeG}InU~9{!kmpC?8XWBTyaZBAfZ+@c!){a1VJ2-E+xaLkgc$49SvoI{!A4C3<)^1!jW8Z znMYag8LD!{X^WI`x9i_Gl*j3%zdD%Shf(itJVb&2V~K$O_a&yi@j&+lFKpNaZynKutEYZ(d?Ct$slNLQ zX3I{xK*(vFB*kC~C<=S0=&xxfDmqW3H2JKOKD*3>P&~&YZhl`; zvVbs)a%r~(iOsbvlG}@?Cnpmk9Fz_yxVu=&?G%sve^)b3Qmm)B8>t2v?Gg=WzeR+c zs&+8qQ6vj_logU2`+m*YRq|yAp4i;SqpGb7p7E0>^tNNdoM@GzNWR0)OK#?YHfE=_ zlFF$U$E~OLe=?0+72z?s?ysI*<9^b5yS~NftM7S*eE6RlU+sFiBGa@Q2GokCmQZoDW-CMp6Jq-&D9tp(sA{;KggUCH`k*_4D`V65l@& z?=8EVBEcxolD>7ct$-kPagJml^zTb#2&KjaIxW&&|LckELYzbgp(j4^VKbCr!(@|6 zAPt26p%`zJ?#e<~Rh$8F-Fonxq1{5Mlsk&ir$uUU8Hs5qG{|}g1194z`%Pl~2&D8)B6oCa@ZB6yx%brS zC^1z`kLf4yoXv$hY`Tk)(_-1VXCw)sg%>I5pw^QJ-~XvF-$H@eQPaSKWr)$T#wt?2>ayj1;aEj z{wmP1n52(%TjOK9-ylm7$*slu7QU0@WHG`beIzMMnDGRN*MN{cR2%XNcxmP{6c=U-1mI Date: Wed, 18 Jan 2023 17:23:33 +0800 Subject: [PATCH 04/46] code clean Signed-off-by: Billy Zha --- test/e2e/internal/utils/match/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/internal/utils/match/status.go b/test/e2e/internal/utils/match/status.go index 2204626a0..ebbdd0fd4 100644 --- a/test/e2e/internal/utils/match/status.go +++ b/test/e2e/internal/utils/match/status.go @@ -140,7 +140,7 @@ func (s *statusMatcher) switchState(st status, key StateKey) { // find next e := findState(now, s.edges[st]) - gomega.Expect(e).NotTo(gomega.BeNil(), fmt.Sprintf("should state node not matching for %v, %v", st, key)) + gomega.Expect(e).NotTo(gomega.BeNil(), fmt.Sprintf("state node not matching for %v, %v", st, key)) // switch s.states[key] = e.to From f03a6aa68e7753ab25db3b589dbc771974ea6090 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 17:25:13 +0800 Subject: [PATCH 05/46] add mount data for fallback testing Signed-off-by: Billy Zha --- test/e2e/suite/command/cp.go | 45 +++++++++--------- .../mount/artifacts_fallback.tar.gz | Bin 0 -> 1449 bytes 2 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 test/e2e/testdata/distribution/mount/artifacts_fallback.tar.gz diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index 5488ec172..b639f3754 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -52,7 +52,7 @@ var _ = Describe("ORAS beginners:", func() { }) var ( - foobarImageStates = []match.StateKey{ + foobarStates = []match.StateKey{ {Digest: "44136fa355b3", Name: "application/vnd.unknown.config.v1+json"}, {Digest: "fcde2b2edba5", Name: "bar"}, {Digest: "2c26b46b68ff", Name: "foo1"}, @@ -64,8 +64,8 @@ var ( {Digest: "2dbea575a349", Name: "application/vnd.oci.artifact.manifest.v1+json"}, } foobarFallbackReferrersStates = []match.StateKey{ - {Digest: "611c75845938", Name: "application/vnd.oci.image.manifest.v1+json"}, - {Digest: "4079617a0647", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "0e007dcb9ded7", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "32b78bd00723ccd .", Name: "application/vnd.oci.image.manifest.v1+json"}, } ) @@ -79,43 +79,43 @@ var _ = Describe("Common registry users:", func() { It("should copy an image to a new repository via tag", func() { src := Reference(Host, ImageRepo, FoobarImageTag) dst := Reference(Host, cpTestRepo("copy-tag"), "copiedTag") - ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() + validate(src, dst) + }) + + It("should copy an image to a new repository via digest", func() { + src := Reference(Host, ImageRepo, FoobarImageDigest) + dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") + ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(src, dst) }) It("should copy an image to a new repository via tag without tagging", func() { src := Reference(Host, ImageRepo, FoobarImageTag) dst := Reference(Host, cpTestRepo("copy-no-tagging"), FoobarImageDigest) - ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(src, dst) }) It("should copy an image and its referrers to a new repository", func() { - stateKeys := append(append(foobarImageStates, foobarReferrersStates...), foobarFallbackReferrersStates...) + stateKeys := append(append(foobarStates, foobarReferrersStates...), foobarFallbackReferrersStates...) src := Reference(Host, ArtifactRepo, FoobarImageTag) - dst := Reference(Host, cpTestRepo("copy-no-tagging"), FoobarImageDigest) + dst := Reference(Host, cpTestRepo("copy-referrers"), FoobarImageDigest) ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() validate(src, dst) }) - It("should copy an image to a new repository via digest", func() { - src := Reference(Host, ImageRepo, FoobarImageDigest) - dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") - ORAS("cp", src, dst, "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() - validate(src, dst) - }) - - It("should copy an certain platform of image to a new repository via tag", func() { + It("should copy a certain platform of image to a new repository via tag", func() { src := Reference(Host, ImageRepo, MultiImageTag) - dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") - ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + dst := Reference(Host, cpTestRepo("copy-platform-tag"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) }) - It("should copy an certain platform of image to a new repository via digest", func() { + It("should copy a certain platform of image to a new repository via digest", func() { src := Reference(Host, ImageRepo, MultiImageDigest) - dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") - ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + dst := Reference(Host, cpTestRepo("copy-platform-digest"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) }) @@ -124,13 +124,12 @@ var _ = Describe("Common registry users:", func() { tags := []string{"tag1", "tag2", "tag3"} dstRepo := cpTestRepo("copy-multi-tagging") dst := Reference(Host, dstRepo, "") - ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(foobarImageStates, true, len(foobarImageStates)).Exec() + ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() for _, tag := range tags { dst := Reference(Host, dstRepo, tag) validate(src, dst) } }) - }) }) @@ -142,7 +141,7 @@ var _ = Describe("OCI spec 1.0 registry users:", func() { gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) } It("should copy an image artifact and its referrers to a fallback repository", func() { - stateKeys := append(foobarImageStates, foobarFallbackReferrersStates...) + stateKeys := append(foobarStates, foobarFallbackReferrersStates...) src := Reference(Host, ArtifactRepo, FallbackSbomArtifactDigest) dst := Reference(FallbackHost, cpTestRepo("copy-fallback"), "") ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() diff --git a/test/e2e/testdata/distribution/mount/artifacts_fallback.tar.gz b/test/e2e/testdata/distribution/mount/artifacts_fallback.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ba7c07e67b580031614ca059f7d3b8378ff7d2eb GIT binary patch literal 1449 zcmV;a1y=eWiwFP!000001MQvLQsYJthItgqw_(kkNAgu3fXl61B$Z0d(W_nCq9j2T zp?LR<%pr^I#S)fB1Mcs};1e_RcmH;4TEdp*hpR5njtL4#Dc84w&D-_Qm{Ee5WQ1WQPuhRSyh(JThH6~_y@&4>R-8eQ8(4yQ39dA{zM|sKMI@r-;&`& zpuhf_@(~!H$}J@Bn&$0FR-M+`gzcwQwI4Tg?;)X zcX$YN*Plqq*7^%ULH`(Z&>y)VpEWwS&Pva6!>q|1bE-nD_p5R>zbaNgj`TNLfdyl~)E{&FDi%WhC&JD7)se>Y ziWApAM*dsXsIlh>>?Q&K$ApS)|CeXyD8>Suc>do{YV*x4)gP{^i*hxYk&DUF*+PB2 zyLOY=L|tDm3Zt6v*yZiYPD@ivi=~>o=~Ar<@9JiHi+}rGhnHSVOu6#Kd@{R#^%hP0 zq`H`|RC80gsV^2TJl7WUaDRCCG04Oyk%Yd8~z@7xR@-|U6{{g_Sb#K$*f-xW-<-Iuw!t_5z9lQOOsQ| zyfxfgjk7G%Ipx@9VH}<_o>`-EOOYB^mYREq4a10WN~DlZSj8M-j>(t#34tEE&(Ks(HO_`{5Jcz~gumeIE6Qe6 z&ba(DOn2<;`26hie>%YNz+T_~AM1aCp#G0S2mKEc4bJ!2dBJY}5a7oBvS%9iM3RpDvdlvh?%Za%@mWs1M!K;E!VS{~(DD2KPy7FE zTEX}K80^#El*`~G_MkZEUjI>qX{*1a66*gbbk$!~P2rVk-s1?p^k<HlW8V6Xh= zt^R^D@c$^JaQwGz_WC&%5Drnm;rMS1hRy$jTSdlRj*}yX{@wlmssD!(#9{w03TgO1 z+w}kS{y+Ht@kz}87src2U>7O)KMpnDmj6TlXB3{cf(}jGv8MsNlQcg6q5d0-Vg3K$ zR*|uneaoGQhLK^-L_1{<|=KoOtMJ5IRe^viI`#*#FF9rjh05Ac^`9%kBD)Lj4zmLGvFD|9v=O=-=J{pZEVTWzhc_g(Uo+2=M=rNX!2R zyyz&<3Gn~Z&i@qrKiI&w|HJ-&6q59RXd{mX`2T6=e-i#r>9+q9@c$TmPXC8Cg07_C z|J(iFIOqxc|1p66@6#rQ&wtqe8IM8z|IiDHLERRe82Wdv|DO9lC=Ou(^00000000000000000000;B)aG^0kvI0H6Q> D1iBV< literal 0 HcmV?d00001 From db50edcb2a8359e3ab8642e9b86d2653a941e2bf Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 18:06:25 +0800 Subject: [PATCH 06/46] fix: deduplicate copy status logs Signed-off-by: Billy Zha --- cmd/oras/cp.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index 1a81fdcd2..09338a620 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -113,17 +113,23 @@ func runCopy(opts copyOptions) error { committed := &sync.Map{} extendedCopyOptions := oras.DefaultExtendedCopyOptions extendedCopyOptions.Concurrency = opts.concurrency - extendedCopyOptions.PreCopy = display.StatusPrinter("Copying", opts.Verbose) + extendedCopyOptions.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { + if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + display.PrintStatus(desc, "Copying", opts.Verbose) + } + return nil + } extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { return err } return display.PrintStatus(desc, "Copied ", opts.Verbose) } extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { - committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) - return display.PrintStatus(desc, "Exists ", opts.Verbose) + if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + return display.PrintStatus(desc, "Exists ", opts.Verbose) + } + return nil } var desc ocispec.Descriptor From e4b5deeef745c8b4f1b3a4b5be8ac19eb1b2af42 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 19:03:16 +0800 Subject: [PATCH 07/46] fix copying Signed-off-by: Billy Zha --- cmd/oras/cp.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index 09338a620..d6274ab9d 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -111,22 +111,23 @@ func runCopy(opts copyOptions) error { // Prepare copy options committed := &sync.Map{} + generateContentKey := func(desc ocispec.Descriptor) string { + return desc.Digest.String() + desc.MediaType + } extendedCopyOptions := oras.DefaultExtendedCopyOptions extendedCopyOptions.Concurrency = opts.concurrency - extendedCopyOptions.PreCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { - display.PrintStatus(desc, "Copying", opts.Verbose) - } - return nil - } + extendedCopyOptions.PreCopy = display.StatusPrinter("Copying", opts.Verbose) extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { return err } - return display.PrintStatus(desc, "Copied ", opts.Verbose) + if _, loaded := committed.LoadOrStore(generateContentKey(desc), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + return display.PrintStatus(desc, "Copied ", opts.Verbose) + } + return nil } extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + if _, loaded := committed.LoadOrStore(generateContentKey(desc), desc.Annotations[ocispec.AnnotationTitle]); !loaded { return display.PrintStatus(desc, "Exists ", opts.Verbose) } return nil From 6a5084b4c1f0bed4cb6aff0c8c455ae16fcf6916 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 19:05:05 +0800 Subject: [PATCH 08/46] fix post copy Signed-off-by: Billy Zha --- cmd/oras/cp.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index d6274ab9d..4cf3f6f93 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -118,10 +118,10 @@ func runCopy(opts copyOptions) error { extendedCopyOptions.Concurrency = opts.concurrency extendedCopyOptions.PreCopy = display.StatusPrinter("Copying", opts.Verbose) extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { - return err - } if _, loaded := committed.LoadOrStore(generateContentKey(desc), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { + return err + } return display.PrintStatus(desc, "Copied ", opts.Verbose) } return nil From bbe6b0ce79baa9686f3ddb6e22d36cd92e8726f2 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 19:07:52 +0800 Subject: [PATCH 09/46] bump e2e oras-go Signed-off-by: Billy Zha --- test/e2e/go.mod | 2 +- test/e2e/suite/command/cp.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/go.mod b/test/e2e/go.mod index c60ba5c4d..22e10c8ea 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -6,7 +6,7 @@ require ( github.com/onsi/ginkgo/v2 v2.1.6 github.com/onsi/gomega v1.20.2 github.com/opencontainers/image-spec v1.1.0-rc2 - oras.land/oras-go/v2 v2.0.0-rc.4 + oras.land/oras-go/v2 v2.0.0-20230112153040-29509026fb7f ) require ( diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index b639f3754..6af7501b0 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -69,7 +69,7 @@ var ( } ) -var _ = Describe("Common registry users:", func() { +var _ = Describe("Common registry users:", Focus, func() { When("running `cp`", func() { validate := func(src, dst string) { srcManifest := ORAS("manifest", "fetch", src).Exec().Out.Contents() From f52682c0feb94db6727546e42a10a36330678fec Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 19:31:59 +0800 Subject: [PATCH 10/46] deduplicate based on digest Signed-off-by: Billy Zha --- cmd/oras/cp.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index 4cf3f6f93..5e505e91d 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -111,14 +111,11 @@ func runCopy(opts copyOptions) error { // Prepare copy options committed := &sync.Map{} - generateContentKey := func(desc ocispec.Descriptor) string { - return desc.Digest.String() + desc.MediaType - } extendedCopyOptions := oras.DefaultExtendedCopyOptions extendedCopyOptions.Concurrency = opts.concurrency extendedCopyOptions.PreCopy = display.StatusPrinter("Copying", opts.Verbose) extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(generateContentKey(desc), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { return err } @@ -127,7 +124,7 @@ func runCopy(opts copyOptions) error { return nil } extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(generateContentKey(desc), desc.Annotations[ocispec.AnnotationTitle]); !loaded { + if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { return display.PrintStatus(desc, "Exists ", opts.Verbose) } return nil From 41b7371d4db3b142a13061778bcc9e1f97c17523 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 20:03:25 +0800 Subject: [PATCH 11/46] revert copy change Signed-off-by: Billy Zha --- cmd/oras/cp.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/cmd/oras/cp.go b/cmd/oras/cp.go index 5e505e91d..1a81fdcd2 100644 --- a/cmd/oras/cp.go +++ b/cmd/oras/cp.go @@ -115,19 +115,15 @@ func runCopy(opts copyOptions) error { extendedCopyOptions.Concurrency = opts.concurrency extendedCopyOptions.PreCopy = display.StatusPrinter("Copying", opts.Verbose) extendedCopyOptions.PostCopy = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { - if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { - return err - } - return display.PrintStatus(desc, "Copied ", opts.Verbose) + committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) + if err := display.PrintSuccessorStatus(ctx, desc, "Skipped", dst, committed, opts.Verbose); err != nil { + return err } - return nil + return display.PrintStatus(desc, "Copied ", opts.Verbose) } extendedCopyOptions.OnCopySkipped = func(ctx context.Context, desc ocispec.Descriptor) error { - if _, loaded := committed.LoadOrStore(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]); !loaded { - return display.PrintStatus(desc, "Exists ", opts.Verbose) - } - return nil + committed.Store(desc.Digest.String(), desc.Annotations[ocispec.AnnotationTitle]) + return display.PrintStatus(desc, "Exists ", opts.Verbose) } var desc ocispec.Descriptor From ae063cb3e4530f000f5da02b7758badd0fc79b13 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 20:39:40 +0800 Subject: [PATCH 12/46] add test Signed-off-by: Billy Zha --- test/e2e/internal/utils/testdata.go | 2 +- test/e2e/suite/command/cp.go | 58 +++++++++++++++++++---------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/test/e2e/internal/utils/testdata.go b/test/e2e/internal/utils/testdata.go index e62a561d6..91b53df35 100644 --- a/test/e2e/internal/utils/testdata.go +++ b/test/e2e/internal/utils/testdata.go @@ -25,7 +25,7 @@ const ( MultiImageTag = "multi" MultiImageDigest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" FoobarImageDigest = "sha256:fd6ed2f36b5465244d5dc86cb4e7df0ab8a9d24adc57825099f522fe009a22bb" - FallbackSbomArtifactDigest = "sha256:611c758459382363aaef8862e5884f5887d1f823f257457a508501d46ebbfbdf" + FallbackSignatureArtifactDigest = "sha256:0e007dcb9ded7f49c4dc8e3eed4a446712eb6fdf08a665a4f2352d6d2f8bdf17" MultiImageManifest = `{"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"}}]}` MultiImageDescriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` LinuxAMD64ImageManifest = `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:2ef548696ac7dd66ef38aab5cc8fc5cc1fb637dfaedb3a9afc89bf16db9277e1","size":10240,"annotations":{"org.opencontainers.image.title":"hello.tar"}}]}` diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index 6af7501b0..ec0d84842 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -64,12 +64,21 @@ var ( {Digest: "2dbea575a349", Name: "application/vnd.oci.artifact.manifest.v1+json"}, } foobarFallbackReferrersStates = []match.StateKey{ - {Digest: "0e007dcb9ded7", Name: "application/vnd.oci.image.manifest.v1+json"}, - {Digest: "32b78bd00723ccd .", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "0e007dcb9ded", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "32b78bd00723", Name: "application/vnd.oci.image.manifest.v1+json"}, + } + foobarFallbackConfigStates = []match.StateKey{ + {Digest: "44136fa355b3", Name: "test.signature.file"}, + {Digest: "44136fa355b3", Name: "test.sbom.file"}, + } + multiImageStates = []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"}, } ) -var _ = Describe("Common registry users:", Focus, func() { +var _ = Describe("Common registry users:", func() { When("running `cp`", func() { validate := func(src, dst string) { srcManifest := ORAS("manifest", "fetch", src).Exec().Out.Contents() @@ -78,51 +87,51 @@ var _ = Describe("Common registry users:", Focus, func() { } It("should copy an image to a new repository via tag", func() { src := Reference(Host, ImageRepo, FoobarImageTag) - dst := Reference(Host, cpTestRepo("copy-tag"), "copiedTag") + dst := Reference(Host, cpTestRepo("tag"), "copiedTag") ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(src, dst) }) It("should copy an image to a new repository via digest", func() { src := Reference(Host, ImageRepo, FoobarImageDigest) - dst := Reference(Host, cpTestRepo("copy-digest"), "copiedTag") + dst := Reference(Host, cpTestRepo("digest"), "copiedTag") ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(src, dst) }) It("should copy an image to a new repository via tag without tagging", func() { src := Reference(Host, ImageRepo, FoobarImageTag) - dst := Reference(Host, cpTestRepo("copy-no-tagging"), FoobarImageDigest) + dst := Reference(Host, cpTestRepo("no-tagging"), FoobarImageDigest) ORAS("cp", src, dst, "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() validate(src, dst) }) It("should copy an image and its referrers to a new repository", func() { - stateKeys := append(append(foobarStates, foobarReferrersStates...), foobarFallbackReferrersStates...) + stateKeys := append(append(foobarStates, foobarReferrersStates...), foobarFallbackConfigStates...) src := Reference(Host, ArtifactRepo, FoobarImageTag) - dst := Reference(Host, cpTestRepo("copy-referrers"), FoobarImageDigest) + dst := Reference(Host, cpTestRepo("referrers"), FoobarImageDigest) ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() validate(src, dst) }) It("should copy a certain platform of image to a new repository via tag", func() { src := Reference(Host, ImageRepo, MultiImageTag) - dst := Reference(Host, cpTestRepo("copy-platform-tag"), "copiedTag") - ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() + dst := Reference(Host, cpTestRepo("platform-tag"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(multiImageStates, true, len(multiImageStates)).Exec() validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) }) It("should copy a certain platform of image to a new repository via digest", func() { src := Reference(Host, ImageRepo, MultiImageDigest) - dst := Reference(Host, cpTestRepo("copy-platform-digest"), "copiedTag") - ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() + dst := Reference(Host, cpTestRepo("platform-digest"), "copiedTag") + ORAS("cp", src, dst, "--platform", "linux/amd64", "-v").MatchStatus(multiImageStates, true, len(multiImageStates)).Exec() validate(Reference(Host, ImageRepo, LinuxAMD64ImageDigest), dst) }) It("should copy an image to a new repository with multiple tagging", func() { src := Reference(Host, ImageRepo, FoobarImageDigest) tags := []string{"tag1", "tag2", "tag3"} - dstRepo := cpTestRepo("copy-multi-tagging") + dstRepo := cpTestRepo("multi-tagging") dst := Reference(Host, dstRepo, "") ORAS("cp", src, dst+":"+strings.Join(tags, ","), "-v").MatchStatus(foobarStates, true, len(foobarStates)).Exec() for _, tag := range tags { @@ -140,13 +149,24 @@ var _ = Describe("OCI spec 1.0 registry users:", func() { dstManifest := ORAS("manifest", "fetch", dst).Exec().Out.Contents() gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) } - It("should copy an image artifact and its referrers to a fallback repository", func() { - stateKeys := append(foobarStates, foobarFallbackReferrersStates...) - src := Reference(Host, ArtifactRepo, FallbackSbomArtifactDigest) - dst := Reference(FallbackHost, cpTestRepo("copy-fallback"), "") + It("should copy an image artifact and its referrers to a fallback registry, and back to registry", func() { + repo := cpTestRepo("fallback") + stateKeys := append(append(foobarStates, foobarFallbackReferrersStates...), foobarFallbackConfigStates...) + src := Reference(Host, ArtifactRepo, FallbackSignatureArtifactDigest) + dst := Reference(FallbackHost, repo, "") + dstWithDigest := Reference(FallbackHost, repo, FallbackSignatureArtifactDigest) ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() - validate(src, dst) - }) + validate(src, dstWithDigest) + ORAS("discover", "-o", "tree", Reference(FallbackHost, repo, FoobarImageDigest)). + WithDescription("discover referrer via subject").MatchKeyWords(FallbackSignatureArtifactDigest).Exec() + src = dstWithDigest + dstWithDigest = Reference(Host, repo, FallbackSignatureArtifactDigest) + dst = Reference(Host, repo, "") + ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() + validate(src, dstWithDigest) + ORAS("discover", "-o", "tree", Reference(Host, repo, FoobarImageDigest)). + WithDescription("discover referrer via subject").MatchKeyWords(FallbackSignatureArtifactDigest).Exec() + }) }) }) From be1ffd97f09442cb6e48df580b72449428b787c8 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 20:55:54 +0800 Subject: [PATCH 13/46] fix e2e Signed-off-by: Billy Zha --- test/e2e/internal/utils/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/internal/utils/init.go b/test/e2e/internal/utils/init.go index 1006226bc..f8f7ca94b 100644 --- a/test/e2e/internal/utils/init.go +++ b/test/e2e/internal/utils/init.go @@ -49,7 +49,7 @@ func init() { panic(err) } - FallbackHost = os.Getenv("ORAS_REGISTRY_HOST") + FallbackHost = os.Getenv("ORAS_REGISTRY_HOST_FALLBACK") if FallbackHost == "" { FallbackHost = "localhost:6000" fmt.Fprintln(os.Stderr, "cannot find fallback host name in ORAS_REGISTRY_HOST_FALLBACK, using", FallbackHost, "instead") From a264e846be2ec7f5be28d22d76740c5a02ae2811 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 21:10:53 +0800 Subject: [PATCH 14/46] use billy's fork for testing Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c3030ed2..dbd019c4d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ - ghcr.io/oras-project/registry:v1.0.0-rc.3 + ghcr.io/qweeah/registry:v1.0.0-rc.3 # Prepare Fallback Registry trap 'docker kill oras-e2e-fallback || true' ERR From 5aac9783299dc23de79f34d7314d1cf7f8e354a2 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Wed, 18 Jan 2023 21:16:08 +0800 Subject: [PATCH 15/46] correct version Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dbd019c4d..b23d38cf4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,7 +59,7 @@ jobs: --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ - ghcr.io/qweeah/registry:v1.0.0-rc.3 + ghcr.io/qweeah/registry:v1.0.0-rc.4 # Prepare Fallback Registry trap 'docker kill oras-e2e-fallback || true' ERR From b5429cc49d3362833c9d2148033ffc8e8b3cf276 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Sun, 29 Jan 2023 11:17:28 +0800 Subject: [PATCH 16/46] add test data for fallback registry Signed-off-by: Billy Zha --- .gitignore | 3 +- test/e2e/README.md | 82 +++++++++++++----- .../mount_fallback/artifacts.tar.gz | Bin 0 -> 4378 bytes .../distribution/mount_fallback/passwd_bcrypt | 1 + 4 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 test/e2e/testdata/distribution/mount_fallback/artifacts.tar.gz create mode 100644 test/e2e/testdata/distribution/mount_fallback/passwd_bcrypt diff --git a/.gitignore b/.gitignore index 4b11d53fa..f6a218d54 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ vendor/ _dist/ # Distribution storage files for local E2E testing -test/e2e/testdata/distribution/mount/docker/ +test/e2e/testdata/distribution/mount/docker/ +test/e2e/testdata/distribution/mount_fallback/docker/ diff --git a/test/e2e/README.md b/test/e2e/README.md index 602726835..92b6e4bed 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -15,8 +15,9 @@ go install github.com/onsi/ginkgo/v2/ginkgo@latest ``` If you skip step 2, you can only run tests via `go test`. -### 3. Run Distribution -The backend of E2E test is an [oras-distribution](https://github.com/oras-project/distribution). +### 3. Run Distribution Services +The backend of E2E test depends on two registry services compliant to [OCI distribution spec v1.1-rc1](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md): [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API. The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). +TODO: scriptize this ```shell PORT=5000 docker run -dp $PORT:5000 --rm --name oras-e2e \ @@ -29,7 +30,7 @@ docker run -dp $PORT:5000 --rm --name oras-e2e \ export ORAS_REGISTRY_HOST="localhost:$PORT" # for PowerShell, use $env:ORAS_REGISTRY_HOST = "localhost:$PORT" ``` -If you skipped step 4, E2E test will look for distribution ran in `localhost:5000` +If you skipped step 4, E2E test will look for oras-distribution ran in `localhost:5000` and upstream distribution ran in `localhost:6000` ### 5. _[Optional]_ Setup ORAS Binary for Testing ```bash @@ -92,31 +93,70 @@ Describe: ### 5. Adding New Test Data #### 5.1 Command Suite -Command suite uses pre-baked registry data for testing. The repository name should be `command/$repo_suffix`. To add a new layer, compress the `docker` folder from the root directory of your distribution storage and copy it to `$REPO_ROOT/test/e2e/testdata/distribution/mount` folder. +Command suite uses pre-baked test data, which is a bunch of layered archive files compressed from distribution storage. Test data are all stored in `$REPO_ROOT/test/e2e/testdata/distribution/` but separated in different sub-folders: oras distribution uses `mount` and upstream distribution uses `mount_fallback`. + +For both registries, the repository name should follow the convention of `command/$repo_suffix`. To add a new layer to the test data, compress the `docker` folder from the root directory of the distribution storage and copy it to `$REPO_ROOT/test/e2e/testdata/distribution/mount` folder. ```shell tar -cvzf ${repo_suffix}.tar.gz --owner=0 --group=0 docker/ ``` -Currently we have below OCI images: + + +##### Test Data for ORAS-Distribution ```mermaid graph TD; - subgraph images.tar.gz - A0>tag: multi]-..->A1[oci index] - A1--linux/amd64-->A2[oci image] - A1--linux/arm64-->A3[oci image] - A1--linux/arm/v7-->A4[oci image] - A2-->A5[config1] - A3-->A6[config2] - A4-->A7[config3] - A2-- hello.tar -->A8[blob] - A3-- hello.tar -->A8[blob] - A4-- hello.tar -->A8[blob] - - B0>tag: foobar]-..->B1[oci image] - B1-- foo1 -->B2[blob1] - B1-- foo2 -->B2[blob1] - B1-- bar -->B3[blob2] + subgraph "repository: command/images" + subgraph "file: images.tar.gz" + direction TB + A0>tag: multi]-..->A1[oci index] + A1--linux/amd64-->A2[oci image] + A1--linux/arm64-->A3[oci image] + A1--linux/arm/v7-->A4[oci image] + A2-->A5(config1) + A3-->A6(config2) + A4-->A7(config3) + A2-- hello.tar -->A8(blob) + A3-- hello.tar -->A8(blob) + A4-- hello.tar -->A8(blob) + + B0>tag: foobar]-..->B1[oci image] + B1-- foo1 -->B2(blob1) + B1-- foo2 -->B2(blob1) + B1-- bar -->B3(blob2) + end + end + + subgraph "repository: command/artifacts" + subgraph "file: artifacts.tar.gz" + direction TB + C0>tag: foobar]-..->C1[oci image] + + direction TB + E1["test.sbom.file(artifact)"] -- subject --> C1 + E2["test.signature.file(artifact)"] -- subject --> E1 + end + subgraph "file: artifacts_fallback.tar.gz" + direction TB + D1["test.sbom.file(image)"] -- subject --> C1 + D2["test.signature.file(image)"] -- subject --> D1 + end end ``` +##### Test Data for Upstream Distribution +```mermaid +graph TD; + subgraph "repository: command/artifacts" + subgraph "file: artifacts_fallback.tar.gz" + direction TB + A0>tag: foobar]-..->A1[oci image] + A1-- foo1 -->A2(blob1) + A1-- foo2 -->A2(blob1) + A1-- bar -->A3(blob2) + + E1["test.sbom.file(image)"] -- subject --> C1 + E2["test.signature.file(image)"] -- subject --> E1 + end + end +``` #### 5.2 Scenario Suite Test files used by scenario-based specs are placed in `$REPO_ROOT/test/e2e/testdata/files`. \ No newline at end of file diff --git a/test/e2e/testdata/distribution/mount_fallback/artifacts.tar.gz b/test/e2e/testdata/distribution/mount_fallback/artifacts.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..e0ca94ab56c12a5c57b333dfa28b8bded5e4fb4c GIT binary patch literal 4378 zcmZ`+dpuP8*H(#?Oe904(S>w#$*Cl#;U{;YNU1cGlrAKcOWH9pg(%9U+mKt5s6vPFf`^bgd?I`!MIn~qbQMAKAv*znE28bT4vP#(b$^Gd=$J1+lwV|+j@r0;-dlBR<=#4U0zGnq1K4k zT`y>Fg80oRUL8cZ{-XO}Y)P7+^=;vG@dq2oujDJbD6%U>X(KN%R{4N;=;#DlO}~>u zSDhfW*PaQkdF@()WvF45f8LouLpa6*@t>unKhf5uI${ThY3N6HM0TQ+nK)2XstJrT zxaE`%noC`gLvg%EJJjh)aq}uv_SJ9LtMa%noV|3$-Dj41&P;!siowz_ORu}pV*YUv zeYaH{ru*_jh(O3iH;}zA-qFSbQc+#yeb$1VBHrLo12_CO3jdUDJ#y38z{YqrGPS5x`VQ!U(LU<@TUHSvsxDFE9~g+NWX_k zT4Y89I9={__G!yGl`EC|d^GT&nA$-pSP(W8JL5gDPb0FBHHFfIjmXnnxZ8`!)|~AW zDqM*e;)Bgsm=v30AR#i*vf4jf5UY{#XuxzImmb-au#d`9DA38Cx*Im~A@R72=E1C< z(xeM@Q|K-uA{#|u7Gb(#W-nay%mcOagJ|j$g)VHeap^t^9iw5vuZnGvlOY=%ACBgLl~m;N98;E`ObIg zmks@}%3~sWn+c}f34N)2%1>DM6zpRt?kn*s1Ilqq0sgg5p#S=sH}4v7ON#Gllb^49 zne^q`Z<8SAx!iZD7r#E=&g*aIziQ`_2hOUn6M0KOgVsIuzT{gMzb_c4p{zCt(v7M$ zO-7SlLd3m$A2Y~bBKqo&Cezr$XTKp1h$A!bW#yCfZ~r0Faw(TLxA|(luscYO&rQ(U z<{Y+OKA<`iw>YDe>|x6556M1H!)_JvpoKb{spZj*H+;I;*$Nj8^HnDyb#w2tZs~1W zPadWRxGU|r8n-Q1O;Yvj_4Nn5ZZ@qCcipsCCbF-F)4ymAS@r8KX*Z@;3@<8~wmyL) zih8NqozXW~-#6xRsAcl>Fgq&f%bk<^8`|5;_mH@shMKdGR+bO>VuKIH58hT|cwncs zL3@spfu?hp`qABM_N&tCG!}iQ^R+_9*Q`Batjms^shO%psgtV$OZfy14yPpdy%4H0S|jmOZzmG41$sViiUU8uupyPI*YragYX zd!lj#sR3aI31e))_7>oj@L0vu%uT@<+A5&<;j@S_pee0(pI`M`ky83*=R~zI-Rln~ z$|T6^w^Cjm`9%6E)1=8MiM(p99)87q2r_2dfnNCuVD$k zO;6Dw_I}{)RQul9nGu;@(UXBEj(13Ui-#ILxP?KdncFc=m7y0C)^)AI_eKmN_WGrG z&G)!#RTT*b->eTRqx-j}-w%y^By|4X6EaMWla9Y+zVM~&qX?ht#@T7Th$$OMF&c%y zE=oD9!MTac#*w|^JQX5`aa9PuWmUH}bkiq~V|#o`j(>0raV_03$3V|PzrlWH&ZJv@ zWGgv{a#T*`{)$s3qu@0mK;?I~A;--|rzL?kXVKX!S_i5=Dlv_ediW8O8=Vua4qJ`{ zakg=CEw-BNY2G4#_{s~mt9dTy*f{(b7V56na4nsf+^=6ju%eIDaCoxvf~(D?kkS?5 z**UTI!%pRzU+xgKX|}Km_BTYua=t_zjESjy+R28^Jr%&`g^2A&DRM+2QoC%Pm_>f% ztWwiZwHi-#Kk07!G5=2!-5<%kH7EL_v<>IVyx)_!KCKsmMf4eFe5$6s82PK+ks!*L zg|XGf6L8ubEp|>hy{+1@0<<^mp(o1t+l*zo0&u!mz({ZyY zH*RD(Irq0(4Ot%TP-I;ePO;a-h^_yF5I@lo^IS_)K8J?wQcGEEqqchvB^cv5)sI8K^f z10T9@W!z3cL&sBK+aMa9A|{2u<)BtioGtup+g3%b@rrXwwp=pXH+*)V^+TDc-W5r= z-CJeV`<2UXJwCb7)8DZGKSB|SSvh8-)U+uQebx~5#T?$lDP|ke^KW7>ec%2R>FxL! zFQ=w1ueVEJP$>$G$oW%rr}O>ySAD}H@f<<#kMib*Zc*!J?Y8oT-1?zJrsqSv(mfB@ z8ho1BZZYGJ852wcL7)vr)^kS`HF2spv6iUs0s9z^m&;*UqlluHlqhSj-6pf#JVxnJ zCP=q?V2o7axm-SS#;U?KU8|`})>!5JEXHp`SYl3{b$n>$shw`Nf$V43KFDl{>d{j6 zADIF@to?Sgko2z*bbOs#;W(vVOjRtV!SDY3CU%C*cSR1mW!=KJPjOMLsTTE*6(b=y zGj$h6dOzJ4SSwI3R(?^m(cB{QgrEJgQ!V966dP%Wl4piapw||8{od|Fwd)VN|7TeV zF6tC}`yVH$650#ALTUs!h*{dI{Z#T0M+lFg02a<=m<`g*McbZ5BwmHAC))!fA>$eO z`T6#XE(tkzmQTU6Z=T=1V!IhIPDSXxt&2hKk#(58RRNr%25GJ?PB4I<`?}Z*~ zBy(Opx_vZoSdg|??Vw+D`yAtrl@rh!HvxZyj-fvj#@n@Bh=jX@c6^$qVH*@NZs6VbrnM_u5h1u<74Ih~-!uRJ35=IjHb z2o^^;ip4_^R5W$usd(}*{K#{m$jp@YyB=iWYrS;)R&u(+jn%v8WL@zri_CKu{$*7J z1HJF2uF-1GR#)Ei<9|`qcXP;$*<<5rl~f?_y>eoZ+Ktm1+Ce#06R#w|u={dZ^bkW- zS|Qa)!1^htz)92^r{!$G*n};SVts~5w@r01s+T;Hwf$&;=C+>S^Ndr}N z3#4V^(%~T!v9))Wph=r$0#Yz82T_;hAkl;lGE9cy<(F#RNSzA&v#bctxDg5vJ~{y? z<3@}8WLbb{o|GTHZf<2460f#&WJ^T{xIx}pXo0lG@+p$eD1XXszJ#Y(nqG}l{m1%O zsYv;(SDv7Jfq9vi#wp?3$5|J0;r5IHs?;zagneFyhqZt2rylb-0G>cov~`@>Qmpds z#&{Dr;O&vsLh1{P_%SrowF9n*?bZ^_O(kJj;=Dz}eC8OMFfB)8)ND`CR$@JH| zvT`5|rr*c&k+Ff&TvjMgsvJKuhKhuwvVv5i=Y$Kaf&EQ~U=5tKBniO~y82PK0s@kq z_?FhF16%0&aodH{-{?BSYNlMHP*vYsXNal+uc=_1|B1W{XBbn2gcz>-#G&>f8wQMl z$wp#@-kMY>lSR4^c<^8K!~aZ3Z~ZlEh*nP4P#+!*zLr-8bF^DLtpu-9*T z+nIaEcxkmf4lhTc?10g=7%CfK_4!?0UC{8ME<33-o zU1M2Et%ipBs`*lfrxO%{4pe5W!psSn6T7}5cgHF8593U!SvQC>i#XIrc(Z`3d8z_3 z4ZJuW3(P;7!!#1MIbinix`|)se-a9(B%evxSq3LRwpM?-DnP!?+1s>6cWp-W9Nis9 z_0q&7imWKM7Tz_&lFT(wp7OJu_XY?ktB7{ez9S@I!`41T`P3Mi9nFI-0U~0ZmYVR?pDq?{8^9f&{9LD1t=;+J}*?Pf&8+3ND_11fb6B%iljC& z@M$^`m5fo{gBoENLm%PRHK5b+4RKH{`h_=Htyqu)LX$bE}{rg@@SxUjgF$ zZ0#@0S)G`@TjSP)R^6&oyWO_$<0@D%A8b@o^nIdvDNewq0~ zlR6YLGo}&IIF4r1Yq!AeOJA&u31y>2v7c7=edVtaFQ%0G*%qX16)rVGlh%NKO5c})2fqn)>fnY~9 zZ01n)vjjB7;zoo}Zx@OM$oHEA>R@D4qMQ!wVF>Am7#^xls0cPdosYSFBoN;R^`V2P zeF~q&-bulzS;APe@n%4>E?p1M4eFnscXKkaZjk!^op4@h!9s1R0SFWkbklz4B(~#G zE&Pbpc|%9x?*Qln0n<#phtKr5A(tkNWeJ$)8pREP%_d;SXASEob*f|8J#m|BzJinN-&g|F(KFKziaWm46HY=4+;IM%Frbh d?+_9j=DPaf+JB!z@7@LJP5G9E5;`*_{s%P1q!IuC literal 0 HcmV?d00001 diff --git a/test/e2e/testdata/distribution/mount_fallback/passwd_bcrypt b/test/e2e/testdata/distribution/mount_fallback/passwd_bcrypt new file mode 100644 index 000000000..71e4a0113 --- /dev/null +++ b/test/e2e/testdata/distribution/mount_fallback/passwd_bcrypt @@ -0,0 +1 @@ +hello:$2y$05$7VA2NX15rMmt6Y14Ep7wReDybGvD8e7ko9tsxmvZQwnC4wBfG9Thy From c962337abefa01e45dc4b271f98f0fdcadbd3464 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 13:48:10 +0800 Subject: [PATCH 17/46] add test scripts for e2e Signed-off-by: Billy Zha --- .github/workflows/build.yml | 48 ++++++++++-------------- test/e2e/scripts/test.sh | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 29 deletions(-) create mode 100644 test/e2e/scripts/test.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b23d38cf4..cf1d7d8b2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,41 +43,31 @@ jobs: run: make test - name: Run E2E Tests run: | + # install deps cd $GITHUB_WORKSPACE/test/e2e go install github.com/onsi/ginkgo/v2/ginkgo - mnt_root="$GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount" - - # Prepare Registry - rm -rf $mnt_root/docker - for layer in $(ls -rt $mnt_root/*.tar.gz); do - tar -xvzf $layer -C $mnt_root - done - trap 'docker kill oras-e2e || true' ERR - docker run --pull always -d -p 5000:5000 --rm --name oras-e2e \ - --env REGISTRY_STORAGE_DELETE_ENABLED=true \ - --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ - --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ - --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ - --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ - ghcr.io/qweeah/registry:v1.0.0-rc.4 - - # Prepare Fallback Registry - trap 'docker kill oras-e2e-fallback || true' ERR - docker run --pull always -d -p 6000:5000 --rm --name oras-e2e-fallback \ - --env REGISTRY_STORAGE_DELETE_ENABLED=true \ - --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ - --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ - --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ - registry - # Test and Cleanup + # prepare test data + source $GITHUB_WORKSPACE/test/e2e/scripts/test.sh + run-registry \ + $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount \ + ghcr.io/oras-project/registry:v1.0.0-rc.4 \ + oras-e2e \ + $ORAS_REGISTRY_PORT + run-registry \ + $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount_fallback \ + registry:2.8.1 \ + oras-e2e-fallback \ + $ORAS_REGISTRY_FALLBACK_PORT + + # test ginkgo -r -p --succinct suite - docker kill oras-e2e || true - docker kill oras-e2e-fallback || true env: ORAS_PATH: bin/linux/amd64/oras - ORAS_REGISTRY_HOST: localhost:5000 - ORAS_REGISTRY_HOST_FALLBACK: localhost:6000 + ORAS_REGISTRY_PORT: 5000 + ORAS_REGISTRY_FALLBACK_PORT: 6000 + ORAS_REGISTRY_HOST: localhost:${ORAS_REGISTRY_PORT} + ORAS_REGISTRY_HOST_FALLBACK: localhost:${ORAS_REGISTRY_FALLBACK_PORT} - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/test.sh new file mode 100644 index 000000000..7f21ea798 --- /dev/null +++ b/test/e2e/scripts/test.sh @@ -0,0 +1,75 @@ +#!/bin/sh -e + +function help { + echo "Usage" + echo " run-registry " + echo "" + echo "Arguments" + echo " mount-root root mounting directory for pre-baked registry storage files." + echo " image-name image name of the registry." + echo " container-name container name of the registry service." + echo " container-port port to export the registry service." +} + +# run registry service for testing +function run-registry { + # check arguments + mnt_root=$1 + if [ -z "$mnt_root" ]; then + echo "mount root directory path is not provided." + help + exit 1 + fi + mnt_root=$(realpath --canonicalize-existing ${mnt_root}) + img_name=$2 + if [ -z "$img_name" ]; then + echo "distribution image name is not provided." + help + exit 1 + fi + ctr_name=$3 + if [ -z "$ctr_name" ]; then + echo "container name is not provided." + help + exit 1 + fi + ctr_port=$4 + if [ -z "$ctr_port" ]; then + echo "distribution port is not provided." + help + exit 1 + fi + + # prepare test data + rm -rf $mnt_root/docker + for layer in $(ls -rt $mnt_root/*.tar.gz); do + tar -xvzf $layer -C $mnt_root + done + + try-clean-up $ctr_name + echo "--------------------" + docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ + --env REGISTRY_STORAGE_DELETE_ENABLED=true \ + --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ + --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ + --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ + --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ + $img_name + defer-clean-up $ctr_name +} + +# deferred clean up +function defer-clean-up { + trap "try-clean-up $1" EXIT +} + + +# clean up +function try-clean-up { + ctr_name=$1 + if [ -z "$ctr_name" ]; then + echo "container name for cleanning up is not provided." + exit 1 + fi + trap "docker kill ${ctr_name} || true" +} \ No newline at end of file From bffc5b97317e723eb242980ba6457f3daabd8e73 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 13:55:59 +0800 Subject: [PATCH 18/46] add logs Signed-off-by: Billy Zha --- test/e2e/scripts/test.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/test.sh index 7f21ea798..ba5969c95 100644 --- a/test/e2e/scripts/test.sh +++ b/test/e2e/scripts/test.sh @@ -47,7 +47,7 @@ function run-registry { done try-clean-up $ctr_name - echo "--------------------" + echo "--------tried----------" docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ @@ -67,9 +67,10 @@ function defer-clean-up { # clean up function try-clean-up { ctr_name=$1 + echo "------trying-----------" if [ -z "$ctr_name" ]; then echo "container name for cleanning up is not provided." exit 1 fi - trap "docker kill ${ctr_name} || true" + docker kill ${ctr_name} || true } \ No newline at end of file From bbbf9a68449ecaadff86392a35c583c35a849d70 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 13:59:30 +0800 Subject: [PATCH 19/46] correct git action variables Signed-off-by: Billy Zha --- .github/workflows/build.yml | 4 ++-- test/e2e/scripts/test.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48d9b82f0..91310ae50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,8 +66,8 @@ jobs: ORAS_PATH: bin/linux/amd64/oras ORAS_REGISTRY_PORT: 5000 ORAS_REGISTRY_FALLBACK_PORT: 6000 - ORAS_REGISTRY_HOST: localhost:${ORAS_REGISTRY_PORT} - ORAS_REGISTRY_HOST_FALLBACK: localhost:${ORAS_REGISTRY_FALLBACK_PORT} + ORAS_REGISTRY_HOST: localhost:{{ORAS_REGISTRY_PORT}} + ORAS_REGISTRY_HOST_FALLBACK: localhost:{{ORAS_REGISTRY_FALLBACK_PORT}} - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/test.sh index ba5969c95..6d842aadf 100644 --- a/test/e2e/scripts/test.sh +++ b/test/e2e/scripts/test.sh @@ -47,7 +47,8 @@ function run-registry { done try-clean-up $ctr_name - echo "--------tried----------" + echo $ORAS_REGISTRY_FALLBACK_PORT + echo $ORAS_REGISTRY_PORT docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ @@ -67,7 +68,6 @@ function defer-clean-up { # clean up function try-clean-up { ctr_name=$1 - echo "------trying-----------" if [ -z "$ctr_name" ]; then echo "container name for cleanning up is not provided." exit 1 From 4c6ad45d77486aa2c949c974363d7813d71bf510 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:01:43 +0800 Subject: [PATCH 20/46] add action logs Signed-off-by: Billy Zha --- test/e2e/scripts/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/test.sh index 6d842aadf..e36025c59 100644 --- a/test/e2e/scripts/test.sh +++ b/test/e2e/scripts/test.sh @@ -47,8 +47,8 @@ function run-registry { done try-clean-up $ctr_name - echo $ORAS_REGISTRY_FALLBACK_PORT - echo $ORAS_REGISTRY_PORT + echo $ORAS_REGISTRY_HOST_FALLBACK + echo $ORAS_REGISTRY_HOST docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ From 755a4364dbade69d30bdd9f0352df4317cd2eb1d Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:06:19 +0800 Subject: [PATCH 21/46] rewrite host names Signed-off-by: Billy Zha --- .github/workflows/build.yml | 7 ++++--- test/e2e/scripts/test.sh | 2 -- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 91310ae50..5600d81a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,18 +47,21 @@ jobs: cd $GITHUB_WORKSPACE/test/e2e go install github.com/onsi/ginkgo/v2/ginkgo - # prepare test data + # start registries source $GITHUB_WORKSPACE/test/e2e/scripts/test.sh run-registry \ $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount \ ghcr.io/oras-project/registry:v1.0.0-rc.4 \ oras-e2e \ $ORAS_REGISTRY_PORT + ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" + run-registry \ $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount_fallback \ registry:2.8.1 \ oras-e2e-fallback \ $ORAS_REGISTRY_FALLBACK_PORT + ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" # test ginkgo -r -p --succinct suite @@ -66,8 +69,6 @@ jobs: ORAS_PATH: bin/linux/amd64/oras ORAS_REGISTRY_PORT: 5000 ORAS_REGISTRY_FALLBACK_PORT: 6000 - ORAS_REGISTRY_HOST: localhost:{{ORAS_REGISTRY_PORT}} - ORAS_REGISTRY_HOST_FALLBACK: localhost:{{ORAS_REGISTRY_FALLBACK_PORT}} - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/test.sh index e36025c59..3d9945db9 100644 --- a/test/e2e/scripts/test.sh +++ b/test/e2e/scripts/test.sh @@ -47,8 +47,6 @@ function run-registry { done try-clean-up $ctr_name - echo $ORAS_REGISTRY_HOST_FALLBACK - echo $ORAS_REGISTRY_HOST docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ From e1dec73fef313666f6e9185a574bcc45e47b58bf Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:31:10 +0800 Subject: [PATCH 22/46] use shell script to run tests Signed-off-by: Billy Zha --- .github/workflows/build.yml | 25 +---------------- test/e2e/internal/utils/init.go | 4 +-- test/e2e/scripts/ci.sh | 36 +++++++++++++++++++++++++ test/e2e/scripts/{test.sh => common.sh} | 0 4 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 test/e2e/scripts/ci.sh rename test/e2e/scripts/{test.sh => common.sh} (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5600d81a9..d870595d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,32 +43,9 @@ jobs: run: make test - name: Run E2E Tests run: | - # install deps - cd $GITHUB_WORKSPACE/test/e2e - go install github.com/onsi/ginkgo/v2/ginkgo - - # start registries - source $GITHUB_WORKSPACE/test/e2e/scripts/test.sh - run-registry \ - $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount \ - ghcr.io/oras-project/registry:v1.0.0-rc.4 \ - oras-e2e \ - $ORAS_REGISTRY_PORT - ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" - - run-registry \ - $GITHUB_WORKSPACE/test/e2e/testdata/distribution/mount_fallback \ - registry:2.8.1 \ - oras-e2e-fallback \ - $ORAS_REGISTRY_FALLBACK_PORT - ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" - - # test - ginkgo -r -p --succinct suite + $GITHUB_WORKSPACE/test/e2e/ $GITHUB_WORKSPACE env: ORAS_PATH: bin/linux/amd64/oras - ORAS_REGISTRY_PORT: 5000 - ORAS_REGISTRY_FALLBACK_PORT: 6000 - name: Check Version run: bin/linux/amd64/oras version - name: Upload coverage to codecov.io diff --git a/test/e2e/internal/utils/init.go b/test/e2e/internal/utils/init.go index f8f7ca94b..712e16561 100644 --- a/test/e2e/internal/utils/init.go +++ b/test/e2e/internal/utils/init.go @@ -49,10 +49,10 @@ func init() { panic(err) } - FallbackHost = os.Getenv("ORAS_REGISTRY_HOST_FALLBACK") + FallbackHost = os.Getenv("ORAS_REGISTRY_FALLBACK_HOST") if FallbackHost == "" { FallbackHost = "localhost:6000" - fmt.Fprintln(os.Stderr, "cannot find fallback host name in ORAS_REGISTRY_HOST_FALLBACK, using", FallbackHost, "instead") + fmt.Fprintln(os.Stderr, "cannot find fallback host name in ORAS_REGISTRY_FALLBACK_HOST, using", FallbackHost, "instead") } ref.Registry = FallbackHost if err := ref.ValidateRegistry(); err != nil { diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh new file mode 100644 index 000000000..3368e0c2d --- /dev/null +++ b/test/e2e/scripts/ci.sh @@ -0,0 +1,36 @@ +#!/bin/sh -e + +export ORAS_REGISTRY_PORT="5000" +export ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" +export ORAS_REGISTRY_PORT="6000" +exportORAS_REGISTRY_FALLBACK_HOST="localhost:${ORAS_REGISTRY_FALLBACK_PORT}" + +repo_root=$1 +if [ -z "$repo_root" ]; then + echo "repository root path is not provided." + echo "Usage" + echo " ci.sh " + exit 1 +fi + + # install deps +repo_root=$(realpath --canonicalize-existing ${repo_root}) +cd $repo_root/test/e2e +go install github.com/onsi/ginkgo/v2/ginkgo + + # start registries +source $repo_root/test/e2e/scripts/common.sh +run-registry \ + $repo_root/test/e2e/testdata/distribution/mount \ + ghcr.io/oras-project/registry:v1.0.0-rc.4 \ + oras-e2e \ + $ORAS_REGISTRY_PORT + +run-registry \ + $repo_root/test/e2e/testdata/distribution/mount_fallback \ + registry:2.8.1 \ + oras-e2e-fallback \ + $ORAS_REGISTRY_FALLBACK_PORT + +# run tests +ginkgo -r -p --succinct suite \ No newline at end of file diff --git a/test/e2e/scripts/test.sh b/test/e2e/scripts/common.sh similarity index 100% rename from test/e2e/scripts/test.sh rename to test/e2e/scripts/common.sh From ceb86de133e881834383ed6314ae96270246c706 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:33:49 +0800 Subject: [PATCH 23/46] fix typo Signed-off-by: Billy Zha --- test/e2e/scripts/ci.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index 3368e0c2d..8717f9861 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -3,7 +3,7 @@ export ORAS_REGISTRY_PORT="5000" export ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" export ORAS_REGISTRY_PORT="6000" -exportORAS_REGISTRY_FALLBACK_HOST="localhost:${ORAS_REGISTRY_FALLBACK_PORT}" +export ORAS_REGISTRY_FALLBACK_HOST="localhost:${ORAS_REGISTRY_FALLBACK_PORT}" repo_root=$1 if [ -z "$repo_root" ]; then @@ -15,8 +15,7 @@ fi # install deps repo_root=$(realpath --canonicalize-existing ${repo_root}) -cd $repo_root/test/e2e -go install github.com/onsi/ginkgo/v2/ginkgo +cd $repo_root/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo # start registries source $repo_root/test/e2e/scripts/common.sh From 94f029faa29c829149d3e335d4a50beb56159824 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:37:27 +0800 Subject: [PATCH 24/46] correct ci path Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d870595d5..12997e76c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: run: make test - name: Run E2E Tests run: | - $GITHUB_WORKSPACE/test/e2e/ $GITHUB_WORKSPACE + $GITHUB_WORKSPACE/test/e2e/ci.sh $GITHUB_WORKSPACE env: ORAS_PATH: bin/linux/amd64/oras - name: Check Version From 543657d85ae4a03be59b3088890f95f58cee7319 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 14:53:33 +0800 Subject: [PATCH 25/46] resolve script errors Signed-off-by: Billy Zha --- test/e2e/go.mod | 13 +++++++------ test/e2e/go.sum | 26 ++++++++++++++------------ test/e2e/scripts/ci.sh | 2 +- test/e2e/scripts/common.sh | 2 +- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/test/e2e/go.mod b/test/e2e/go.mod index cc1f1aab4..b34e47a60 100644 --- a/test/e2e/go.mod +++ b/test/e2e/go.mod @@ -3,18 +3,19 @@ module oras.land/oras/test/e2e go 1.20 require ( - github.com/onsi/ginkgo/v2 v2.1.6 - github.com/onsi/gomega v1.20.2 + github.com/onsi/ginkgo/v2 v2.8.0 + github.com/onsi/gomega v1.26.0 github.com/opencontainers/image-spec v1.1.0-rc2 oras.land/oras-go/v2 v2.0.0 ) require ( - github.com/google/go-cmp v0.5.8 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/net v0.5.0 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.4.0 // indirect + golang.org/x/text v0.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/test/e2e/go.sum b/test/e2e/go.sum index af7602d5a..fd89ee311 100644 --- a/test/e2e/go.sum +++ b/test/e2e/go.sum @@ -1,22 +1,24 @@ +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= -github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= +github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index 8717f9861..9a8dbf2c3 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -15,7 +15,7 @@ fi # install deps repo_root=$(realpath --canonicalize-existing ${repo_root}) -cd $repo_root/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo +cd $repo_root/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@v2.8.0 # start registries source $repo_root/test/e2e/scripts/common.sh diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 3d9945db9..232dec9df 100644 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -1,4 +1,4 @@ -#!/bin/sh -e +#!/bin/sh function help { echo "Usage" From eadc5f63a89fa38d330092b177c3fbfe328ad5ff Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 16:06:36 +0800 Subject: [PATCH 26/46] correct test scripts Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- test/e2e/scripts/ci.sh | 37 +++++++++++++++++++++++-------------- test/e2e/scripts/common.sh | 28 +++++++++++----------------- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 12997e76c..ff13e031c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: run: make test - name: Run E2E Tests run: | - $GITHUB_WORKSPACE/test/e2e/ci.sh $GITHUB_WORKSPACE + $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE env: ORAS_PATH: bin/linux/amd64/oras - name: Check Version diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index 9a8dbf2c3..c6cdbc071 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -2,34 +2,43 @@ export ORAS_REGISTRY_PORT="5000" export ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" -export ORAS_REGISTRY_PORT="6000" +export ORAS_REGISTRY_FALLBACK_PORT="6000" export ORAS_REGISTRY_FALLBACK_HOST="localhost:${ORAS_REGISTRY_FALLBACK_PORT}" repo_root=$1 -if [ -z "$repo_root" ]; then +if [ -z "${repo_root}" ]; then echo "repository root path is not provided." echo "Usage" - echo " ci.sh " + echo " ci.sh [clean_up]" exit 1 fi +clean_up=$2 - # install deps +# install deps repo_root=$(realpath --canonicalize-existing ${repo_root}) -cd $repo_root/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@v2.8.0 +cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest - # start registries -source $repo_root/test/e2e/scripts/common.sh -run-registry \ - $repo_root/test/e2e/testdata/distribution/mount \ +# start registries +. ${repo_root}/test/e2e/scripts/common.sh + +if [ "$clean_up" = true ]; then + trap "try_clean_up oras-e2e oras-e2e-fallback" EXIT +fi + +run_registry \ + ${repo_root}/test/e2e/testdata/distribution/mount \ ghcr.io/oras-project/registry:v1.0.0-rc.4 \ oras-e2e \ - $ORAS_REGISTRY_PORT + $ORAS_REGISTRY_HOST -run-registry \ - $repo_root/test/e2e/testdata/distribution/mount_fallback \ +run_registry \ + ${repo_root}/test/e2e/testdata/distribution/mount_fallback \ registry:2.8.1 \ oras-e2e-fallback \ - $ORAS_REGISTRY_FALLBACK_PORT + $ORAS_REGISTRY_FALLBACK_HOST # run tests -ginkgo -r -p --succinct suite \ No newline at end of file +ginkgo -r -p --succinct suite + +# clean up +try_clean_up oras-e2e oras-e2e-fallback \ No newline at end of file diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 232dec9df..949bcaf48 100644 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -1,6 +1,6 @@ -#!/bin/sh +#!/bin/sh -e -function help { +help () { echo "Usage" echo " run-registry " echo "" @@ -12,7 +12,7 @@ function help { } # run registry service for testing -function run-registry { +run_registry () { # check arguments mnt_root=$1 if [ -z "$mnt_root" ]; then @@ -46,7 +46,7 @@ function run-registry { tar -xvzf $layer -C $mnt_root done - try-clean-up $ctr_name + try_clean_up $ctr_name docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ @@ -54,21 +54,15 @@ function run-registry { --mount type=bind,source=$mnt_root/docker,target=/var/lib/registry/docker \ --mount type=bind,source=$mnt_root/passwd_bcrypt,target=/etc/docker/registry/passwd \ $img_name - defer-clean-up $ctr_name -} - -# deferred clean up -function defer-clean-up { - trap "try-clean-up $1" EXIT } # clean up -function try-clean-up { - ctr_name=$1 - if [ -z "$ctr_name" ]; then - echo "container name for cleanning up is not provided." - exit 1 - fi - docker kill ${ctr_name} || true +try_clean_up () { + for ctr_name in "$@" + do + echo "----- try killing ${ctr_name} ------" + docker kill ${ctr_name} || true + echo "------------------------------------" + done } \ No newline at end of file From 615bae153044bd9e825e781f6abc4c4ae1ee15f9 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 16:14:26 +0800 Subject: [PATCH 27/46] update scripts Signed-off-by: Billy Zha --- test/e2e/scripts/ci.sh | 4 ++-- test/e2e/scripts/common.sh | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index c6cdbc071..1f565cf03 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -29,13 +29,13 @@ run_registry \ ${repo_root}/test/e2e/testdata/distribution/mount \ ghcr.io/oras-project/registry:v1.0.0-rc.4 \ oras-e2e \ - $ORAS_REGISTRY_HOST + $ORAS_REGISTRY_PORT run_registry \ ${repo_root}/test/e2e/testdata/distribution/mount_fallback \ registry:2.8.1 \ oras-e2e-fallback \ - $ORAS_REGISTRY_FALLBACK_HOST + $ORAS_REGISTRY_FALLBACK_PORT # run tests ginkgo -r -p --succinct suite diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 949bcaf48..0f84eb660 100644 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -61,8 +61,6 @@ run_registry () { try_clean_up () { for ctr_name in "$@" do - echo "----- try killing ${ctr_name} ------" docker kill ${ctr_name} || true - echo "------------------------------------" done } \ No newline at end of file From e8a920dd1d894f417b1f589f1c045af5b99dde20 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 16:21:24 +0800 Subject: [PATCH 28/46] add sh Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff13e031c..f832e45ab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: run: make test - name: Run E2E Tests run: | - $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE + sh $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE env: ORAS_PATH: bin/linux/amd64/oras - name: Check Version From 767a0366157f421b6903c25f66343ebc1616c38f Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 16:29:08 +0800 Subject: [PATCH 29/46] add license checker Signed-off-by: Billy Zha --- test/e2e/scripts/ci.sh | 13 +++++++++++++ test/e2e/scripts/common.sh | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index 1f565cf03..d6c2a3ccf 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -1,5 +1,18 @@ #!/bin/sh -e +# Copyright The ORAS Authors. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + export ORAS_REGISTRY_PORT="5000" export ORAS_REGISTRY_HOST="localhost:${ORAS_REGISTRY_PORT}" export ORAS_REGISTRY_FALLBACK_PORT="6000" diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 0f84eb660..21c219e08 100644 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -1,5 +1,18 @@ #!/bin/sh -e +# Copyright The ORAS Authors. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + help () { echo "Usage" echo " run-registry " From 88ce722fc072adf21b0dfe7378b79b5ba3d06ec1 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 16:42:26 +0800 Subject: [PATCH 30/46] add cleanup Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- test/e2e/scripts/ci.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f832e45ab..5e014f4d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: run: make test - name: Run E2E Tests run: | - sh $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE + sh $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE --clean env: ORAS_PATH: bin/linux/amd64/oras - name: Check Version diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index d6c2a3ccf..fc4cd5189 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -22,7 +22,7 @@ repo_root=$1 if [ -z "${repo_root}" ]; then echo "repository root path is not provided." echo "Usage" - echo " ci.sh [clean_up]" + echo " ci.sh [--clean]" exit 1 fi clean_up=$2 @@ -34,7 +34,7 @@ cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest # start registries . ${repo_root}/test/e2e/scripts/common.sh -if [ "$clean_up" = true ]; then +if [ "$clean_up" = '--clean' ]; then trap "try_clean_up oras-e2e oras-e2e-fallback" EXIT fi From 5590b9c4810c34fa7ac2360d87e1862a1c60a859 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 17:53:21 +0800 Subject: [PATCH 31/46] add docs Signed-off-by: Billy Zha --- test/e2e/README.md | 74 +++++++++++++------------------------- test/e2e/scripts/ci.sh | 12 +++---- test/e2e/scripts/common.sh | 1 - 3 files changed, 30 insertions(+), 57 deletions(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 92b6e4bed..a2b879365 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -1,68 +1,42 @@ # ORAS End-to-End Testing Dev Guide **KNOWN LIMITATION**: E2E tests are designed to run in the CI and currently only support running on linux platform. -## Setting up -Minimal setup: Run the script in **step 3** +## Prerequisites +[git](https://git-scm.com/downloads), [docker](https://docs.docker.com/desktop/install/linux-install/), [go](https://golang.google.cn/dl/) +## Run E2E Tests via Ginkgo ### 1. Clone Source Code of ORAS CLI ```shell git clone https://github.com/oras-project/oras.git ``` +### 2. Run E2E Tests +Use below command to prepare and run all test suites. +```bash +$REPO_ROOT/test/e2e/scripts/ci.sh $REPO_ROOT --clean # REPO_ROOT is root folder of oras CLI code +``` +If you want to preserve the registry data for further troubleshooting, please remove the `--clean` flag. -### 2. _[Optional]_ Install Ginkgo -This will enable you use `ginkgo` directly in CLI. -```shell -go install github.com/onsi/ginkgo/v2/ginkgo@latest +## Development +### 1. Testing based on your ORAS Binary +By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. To opt-out, set `ORAS_PATH` towards an existing ORAS binary. +```bash +export ORAS_PATH="bin/linux/amd64/oras" # change target platform if needed ``` -If you skip step 2, you can only run tests via `go test`. +You can also customize the testing tool, binary and environments: +### 2. Using `go test` -### 3. Run Distribution Services -The backend of E2E test depends on two registry services compliant to [OCI distribution spec v1.1-rc1](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md): [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API. The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). -TODO: scriptize this -```shell -PORT=5000 -docker run -dp $PORT:5000 --rm --name oras-e2e \ - --env STORAGE_DELETE_ENABLED=true \ - ghcr.io/oras-project/registry:v1.0.0-rc.2 +E2E specs can be ran natively without `ginkgo`: +```bash +# run below command in the target test suite folder +go test oras.land/oras/test/e2e/suite/${suite_name} ``` +This is is super handy for step-by-step debugging when your IDE has a plugin for `go test` -### 4. _[Optional]_ Customize Port for Distribution -```shell -export ORAS_REGISTRY_HOST="localhost:$PORT" -# for PowerShell, use $env:ORAS_REGISTRY_HOST = "localhost:$PORT" -``` -If you skipped step 4, E2E test will look for oras-distribution ran in `localhost:5000` and upstream distribution ran in `localhost:6000` -### 5. _[Optional]_ Setup ORAS Binary for Testing -```bash -# Set REPO_ROOT as root folder of oras CLI code -cd $REPO_ROOT -make build -``` -### 6. _[Optional]_ Setup Pre-Built Binary -You need to setup below environmental variables to debug a pre-built ORAS binary: -```bash -export ORAS_PATH="bin/linux/amd64/oras" # change target platform if needed -export GITHUB_WORKSPACE=$REPO_ROOT -``` -If you skipped step 5 or 6, Gomega will build a temp binary, which will include all the CLI code changes in the working directory. +### 3. Testing Different Distribution Services +The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API; The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). -### 7. _[Optional]_ Mount Test Data -If you want to run command suite, you need to decompress the registry storage files and mount to the distribution. `$REPO_ROOT` points to the root folder of cloned oras CLI code. -```shell -mnt_root=${REPO_ROOT}/test/e2e/testdata/distribution/mount -for layer in $(ls ${mnt_root}/*.tar.gz); do - tar -xvzf $layer -C ${mnt_root} -done - -PORT=5000 -docker run -dp ${PORT}:5000 --rm --name oras-e2e \ - --env STORAGE_DELETE_ENABLED=true \ - --mount type=bind,source=${mnt_root}/docker,target=/opt/data/registry-root-dir/docker \ - ghcr.io/oras-project/registry:v1.0.0-rc.2 -``` -Skipping step 7 you will not be able to run specs in Command suite. +If you want to test against your own registry services, set `ORAS_REGISTRY_HOST` or `ORAS_REGISTRY_FALLBACK_HOST` environmental variables. -## Development ### 1. Constant Build & Watch This is a good choice if you want to debug certain re-runnable specs ```bash diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh index fc4cd5189..4af2abb76 100644 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/ci.sh @@ -27,7 +27,7 @@ if [ -z "${repo_root}" ]; then fi clean_up=$2 -# install deps +echo " === installing ginkgo === " repo_root=$(realpath --canonicalize-existing ${repo_root}) cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest @@ -35,23 +35,23 @@ cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest . ${repo_root}/test/e2e/scripts/common.sh if [ "$clean_up" = '--clean' ]; then + echo " === setting deferred clean up jobs === " trap "try_clean_up oras-e2e oras-e2e-fallback" EXIT fi +echo " === preparing oras distribution === " run_registry \ ${repo_root}/test/e2e/testdata/distribution/mount \ ghcr.io/oras-project/registry:v1.0.0-rc.4 \ oras-e2e \ $ORAS_REGISTRY_PORT +echo " === preparing upstream distribution === " run_registry \ ${repo_root}/test/e2e/testdata/distribution/mount_fallback \ registry:2.8.1 \ oras-e2e-fallback \ $ORAS_REGISTRY_FALLBACK_PORT -# run tests -ginkgo -r -p --succinct suite - -# clean up -try_clean_up oras-e2e oras-e2e-fallback \ No newline at end of file +echo " === run tests === " +ginkgo -r -p --succinct suite \ No newline at end of file diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 21c219e08..a20b5597f 100644 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -53,7 +53,6 @@ run_registry () { exit 1 fi - # prepare test data rm -rf $mnt_root/docker for layer in $(ls -rt $mnt_root/*.tar.gz); do tar -xvzf $layer -C $mnt_root From dcf3101aad67166f3c854a8d911c83d49701ceb4 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 18:03:44 +0800 Subject: [PATCH 32/46] more docs Signed-off-by: Billy Zha --- test/e2e/README.md | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index a2b879365..88be83d4b 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -16,41 +16,36 @@ $REPO_ROOT/test/e2e/scripts/ci.sh $REPO_ROOT --clean # REPO_ROOT is root folder If you want to preserve the registry data for further troubleshooting, please remove the `--clean` flag. ## Development -### 1. Testing based on your ORAS Binary -By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. To opt-out, set `ORAS_PATH` towards an existing ORAS binary. -```bash -export ORAS_PATH="bin/linux/amd64/oras" # change target platform if needed -``` -You can also customize the testing tool, binary and environments: -### 2. Using `go test` +### 1. Using IDEs +Since E2E test suites are added as an nested module, it's go module and checksum files are separated from oras CLI and is stored in `$REPO_ROOT/test/e2e/`. To develop E2E tests, it's better to open the workspace from `$REPO_ROOT/test/e2e/`. +### 2. Testing pre-built ORAS Binary +By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. To run tests more faster, set `ORAS_PATH` towards an pre-built ORAS binary. +### 3. Debugging using `go test` E2E specs can be ran natively without `ginkgo`: ```bash # run below command in the target test suite folder go test oras.land/oras/test/e2e/suite/${suite_name} ``` -This is is super handy for step-by-step debugging when your IDE has a plugin for `go test` +This is is super handy for step-by-step debugging when your IDE has a plugin for `go test` If you need to debug certain specs, use [focused spec](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. -### 3. Testing Different Distribution Services +### 4. Testing Different Distribution Services The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API; The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). If you want to test against your own registry services, set `ORAS_REGISTRY_HOST` or `ORAS_REGISTRY_FALLBACK_HOST` environmental variables. -### 1. Constant Build & Watch +### 5. Constant Build & Watch This is a good choice if you want to debug certain re-runnable specs ```bash cd $REPO_ROOT/test/e2e ginkgo watch -r ``` -### 2. Debugging -Since E2E test suites are added to a sub-module, you need to run `go test` from `$REPO_ROOT/test/e2e/`. If you need to debug a certain spec, use [focused spec](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. - -### 3. Trouble-shooting CLI -Executed command should be shown in the ginkgo logs after `[It]`, +### 6. Trouble-shooting CLI +The executed commands should be shown in the ginkgo logs after `[It]`, with -### 4. Adding New Tests +### 7. Adding New Tests Two suites will be maintained for E2E testing: - command: contains test specs for single oras command execution - scenario: contains featured scenarios with several oras commands execution @@ -64,9 +59,9 @@ Describe: Expect: (detailed checks for execution results) ``` -### 5. Adding New Test Data +### 8. Adding New Test Data -#### 5.1 Command Suite +#### 8.1 Command Suite Command suite uses pre-baked test data, which is a bunch of layered archive files compressed from distribution storage. Test data are all stored in `$REPO_ROOT/test/e2e/testdata/distribution/` but separated in different sub-folders: oras distribution uses `mount` and upstream distribution uses `mount_fallback`. For both registries, the repository name should follow the convention of `command/$repo_suffix`. To add a new layer to the test data, compress the `docker` folder from the root directory of the distribution storage and copy it to `$REPO_ROOT/test/e2e/testdata/distribution/mount` folder. @@ -132,5 +127,5 @@ graph TD; end end ``` -#### 5.2 Scenario Suite +#### 8.2 Scenario Suite Test files used by scenario-based specs are placed in `$REPO_ROOT/test/e2e/testdata/files`. \ No newline at end of file From d625470c25d68176dc842b00a94df756f4186377 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 18:04:28 +0800 Subject: [PATCH 33/46] code clean Signed-off-by: Billy Zha --- test/e2e/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 88be83d4b..eb53dc1d2 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -1,7 +1,7 @@ # ORAS End-to-End Testing Dev Guide **KNOWN LIMITATION**: E2E tests are designed to run in the CI and currently only support running on linux platform. ## Prerequisites -[git](https://git-scm.com/downloads), [docker](https://docs.docker.com/desktop/install/linux-install/), [go](https://golang.google.cn/dl/) +[git](https://git-scm.com/downloads), [docker](https://docs.docker.com/desktop/install/linux-install/), [go](https://golang.google.cn/dl/). ## Run E2E Tests via Ginkgo ### 1. Clone Source Code of ORAS CLI From c919057391d8582f6b595b2318f90b71fc61bc63 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 15:45:19 +0000 Subject: [PATCH 34/46] add execute permission to scripts Signed-off-by: Billy Zha --- test/e2e/scripts/ci.sh | 0 test/e2e/scripts/common.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 test/e2e/scripts/ci.sh mode change 100644 => 100755 test/e2e/scripts/common.sh diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/ci.sh old mode 100644 new mode 100755 diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh old mode 100644 new mode 100755 From a0e2c18e1a40805045ad1e6dd13f34cfc9a5f652 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Thu, 9 Feb 2023 15:56:32 +0000 Subject: [PATCH 35/46] rename script Signed-off-by: Billy Zha --- .github/workflows/build.yml | 2 +- test/e2e/README.md | 25 +++++++++++-------------- test/e2e/scripts/{ci.sh => e2e.sh} | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) rename test/e2e/scripts/{ci.sh => e2e.sh} (97%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e014f4d4..f7bb92c74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,7 +43,7 @@ jobs: run: make test - name: Run E2E Tests run: | - sh $GITHUB_WORKSPACE/test/e2e/scripts/ci.sh $GITHUB_WORKSPACE --clean + sh $GITHUB_WORKSPACE/test/e2e/scripts/e2e.sh $GITHUB_WORKSPACE --clean env: ORAS_PATH: bin/linux/amd64/oras - name: Check Version diff --git a/test/e2e/README.md b/test/e2e/README.md index eb53dc1d2..5181c4241 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -1,19 +1,17 @@ # ORAS End-to-End Testing Dev Guide **KNOWN LIMITATION**: E2E tests are designed to run in the CI and currently only support running on linux platform. ## Prerequisites -[git](https://git-scm.com/downloads), [docker](https://docs.docker.com/desktop/install/linux-install/), [go](https://golang.google.cn/dl/). +Install [git](https://git-scm.com/downloads), [docker](https://docs.docker.com/desktop/install/linux-install/), [go](https://golang.google.cn/dl/). -## Run E2E Tests via Ginkgo -### 1. Clone Source Code of ORAS CLI +## Run E2E Script ```shell -git clone https://github.com/oras-project/oras.git +$REPO_ROOT/test/e2e/scripts/e2e.sh $REPO_ROOT --clean # REPO_ROOT is root folder of oras CLI code +``` + +If the tests fails with errors like `ginkgo: not found`, use below command to add GOPATH into the PATH variable +```shell +PATH+=:$(go env GOPATH)/bin ``` -### 2. Run E2E Tests -Use below command to prepare and run all test suites. -```bash -$REPO_ROOT/test/e2e/scripts/ci.sh $REPO_ROOT --clean # REPO_ROOT is root folder of oras CLI code -``` -If you want to preserve the registry data for further troubleshooting, please remove the `--clean` flag. ## Development ### 1. Using IDEs @@ -23,12 +21,11 @@ By default, Gomega builds a temp binary every time before running e2e tests, whi ### 3. Debugging using `go test` E2E specs can be ran natively without `ginkgo`: -```bash +```shell # run below command in the target test suite folder go test oras.land/oras/test/e2e/suite/${suite_name} ``` -This is is super handy for step-by-step debugging when your IDE has a plugin for `go test` If you need to debug certain specs, use [focused spec](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. - +This is is super handy for step-by-step debugging when your IDE has a plugin for `go test`. If you need to debug certain specs, use [focused spec](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. ### 4. Testing Different Distribution Services The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API; The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). @@ -37,7 +34,7 @@ If you want to test against your own registry services, set `ORAS_REGISTRY_HOST` ### 5. Constant Build & Watch This is a good choice if you want to debug certain re-runnable specs -```bash +```shell cd $REPO_ROOT/test/e2e ginkgo watch -r ``` diff --git a/test/e2e/scripts/ci.sh b/test/e2e/scripts/e2e.sh similarity index 97% rename from test/e2e/scripts/ci.sh rename to test/e2e/scripts/e2e.sh index 4af2abb76..f54859099 100755 --- a/test/e2e/scripts/ci.sh +++ b/test/e2e/scripts/e2e.sh @@ -22,7 +22,7 @@ repo_root=$1 if [ -z "${repo_root}" ]; then echo "repository root path is not provided." echo "Usage" - echo " ci.sh [--clean]" + echo " e2e.sh [--clean]" exit 1 fi clean_up=$2 From bad0fc942d76aca3b07cbbc07b1737ebf25b58ba Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 02:20:46 +0000 Subject: [PATCH 36/46] add container stopping logs Signed-off-by: Billy Zha --- test/e2e/scripts/common.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index a20b5597f..574ab2803 100755 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -71,6 +71,7 @@ run_registry () { # clean up try_clean_up () { + echo " === stopping below containers ===" for ctr_name in "$@" do docker kill ${ctr_name} || true From 78d4c37381921621e76c0b6e9b5bc277e9a95382 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 03:45:14 +0000 Subject: [PATCH 37/46] doc clean Signed-off-by: Billy Zha --- test/e2e/README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 5181c4241..830c1a5ec 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -15,25 +15,26 @@ PATH+=:$(go env GOPATH)/bin ## Development ### 1. Using IDEs -Since E2E test suites are added as an nested module, it's go module and checksum files are separated from oras CLI and is stored in `$REPO_ROOT/test/e2e/`. To develop E2E tests, it's better to open the workspace from `$REPO_ROOT/test/e2e/`. +Since E2E test suites are added as an nested module, the module file and checksum file are separated from oras CLI. To develop E2E tests, it's better to open your IDE or set the working diretory in `$REPO_ROOT/test/e2e/`. + ### 2. Testing pre-built ORAS Binary -By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. To run tests more faster, set `ORAS_PATH` towards an pre-built ORAS binary. -### 3. Debugging using `go test` +By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. If you are only making changes to E2E test code, you may set `ORAS_PATH` towards your pre-built ORAS binary to speed up the testing process +### 3. Debugging via `go test` E2E specs can be ran natively without `ginkgo`: ```shell # run below command in the target test suite folder go test oras.land/oras/test/e2e/suite/${suite_name} ``` -This is is super handy for step-by-step debugging when your IDE has a plugin for `go test`. If you need to debug certain specs, use [focused spec](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. +This is is super handy when you want to step-by-step debugging from command-line or via an IDE. If you need to debug certain specs, use [focused specs](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. ### 4. Testing Different Distribution Services The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API; The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). -If you want to test against your own registry services, set `ORAS_REGISTRY_HOST` or `ORAS_REGISTRY_FALLBACK_HOST` environmental variables. +You can run scenario test suite against your own registry services via setting `ORAS_REGISTRY_HOST` or `ORAS_REGISTRY_FALLBACK_HOST` environmental variables. ### 5. Constant Build & Watch -This is a good choice if you want to debug certain re-runnable specs +This is a good choice if you want to debug certain re-runnable specs: ```shell cd $REPO_ROOT/test/e2e ginkgo watch -r @@ -45,6 +46,7 @@ The executed commands should be shown in the ginkgo logs after `[It]`, with ### 7. Adding New Tests Two suites will be maintained for E2E testing: - command: contains test specs for single oras command execution +- auth: contains test specs similar to command specs but specific to auth. It cannot be ran in parallel with command suite specs - scenario: contains featured scenarios with several oras commands execution Inside a suite, please follow below model when building the hierarchical collections of specs: From 58f71444c6a33656c6ee877393b18760512ee641 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 03:58:42 +0000 Subject: [PATCH 38/46] use pushd instead Signed-off-by: Billy Zha --- test/e2e/scripts/e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/scripts/e2e.sh b/test/e2e/scripts/e2e.sh index f54859099..6064d3e11 100755 --- a/test/e2e/scripts/e2e.sh +++ b/test/e2e/scripts/e2e.sh @@ -29,7 +29,7 @@ clean_up=$2 echo " === installing ginkgo === " repo_root=$(realpath --canonicalize-existing ${repo_root}) -cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest +pushd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest && popd # start registries . ${repo_root}/test/e2e/scripts/common.sh From 1d1786a8791dc040db3dbadcf7580bbd78bf3487 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 04:20:17 +0000 Subject: [PATCH 39/46] run distribution as current user Signed-off-by: Billy Zha --- test/e2e/scripts/common.sh | 1 + test/e2e/scripts/e2e.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/e2e/scripts/common.sh b/test/e2e/scripts/common.sh index 574ab2803..46319be81 100755 --- a/test/e2e/scripts/common.sh +++ b/test/e2e/scripts/common.sh @@ -60,6 +60,7 @@ run_registry () { try_clean_up $ctr_name docker run --pull always -d -p $ctr_port:5000 --rm --name $ctr_name \ + -u $(id -u $(whoami)) \ --env REGISTRY_STORAGE_DELETE_ENABLED=true \ --env REGISTRY_AUTH_HTPASSWD_REALM=test-basic \ --env REGISTRY_AUTH_HTPASSWD_PATH=/etc/docker/registry/passwd \ diff --git a/test/e2e/scripts/e2e.sh b/test/e2e/scripts/e2e.sh index 6064d3e11..eac0a2bf9 100755 --- a/test/e2e/scripts/e2e.sh +++ b/test/e2e/scripts/e2e.sh @@ -29,7 +29,8 @@ clean_up=$2 echo " === installing ginkgo === " repo_root=$(realpath --canonicalize-existing ${repo_root}) -pushd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest && popd +cwd=$(pwd) +cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest && cd $cwd # start registries . ${repo_root}/test/e2e/scripts/common.sh From 2d7aff7222cf5c33a6ef13c6b64e6ae2c2be3f4f Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 04:25:06 +0000 Subject: [PATCH 40/46] add exiting trap Signed-off-by: Billy Zha --- test/e2e/scripts/e2e.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/scripts/e2e.sh b/test/e2e/scripts/e2e.sh index eac0a2bf9..bf9c69b2c 100755 --- a/test/e2e/scripts/e2e.sh +++ b/test/e2e/scripts/e2e.sh @@ -30,7 +30,8 @@ clean_up=$2 echo " === installing ginkgo === " repo_root=$(realpath --canonicalize-existing ${repo_root}) cwd=$(pwd) -cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest && cd $cwd +cd ${repo_root}/test/e2e && go install github.com/onsi/ginkgo/v2/ginkgo@latest +trap "cd $cwd" EXIT # start registries . ${repo_root}/test/e2e/scripts/common.sh From 8276913d0685645c835b87a49042216c42a91796 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 05:11:02 +0000 Subject: [PATCH 41/46] add fallback registry to registry Signed-off-by: Billy Zha --- test/e2e/internal/utils/testdata.go | 43 +++++++++++++++-------------- test/e2e/suite/command/cp.go | 37 ++++++++++++++----------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/test/e2e/internal/utils/testdata.go b/test/e2e/internal/utils/testdata.go index 5a9a65627..2729c8c0e 100644 --- a/test/e2e/internal/utils/testdata.go +++ b/test/e2e/internal/utils/testdata.go @@ -16,24 +16,27 @@ limitations under the License. package utils const ( - PreviewDesc = "** This command is in preview and under development. **" - ExampleDesc = "\nExample - " - ImageRepo = "command/images" - ArtifactRepo = "command/artifacts" - Repo = "command/images" - Namespace = "command" - FoobarImageTag = "foobar" - FoobarConfigDesc = "{\"mediaType\":\"application/vnd.unknown.config.v1+json\",\"digest\":\"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a\",\"size\":2}" - MultiImageTag = "multi" - MultiImageDigest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" - FoobarImageDigest = "sha256:fd6ed2f36b5465244d5dc86cb4e7df0ab8a9d24adc57825099f522fe009a22bb" - FallbackSignatureArtifactDigest = "sha256:0e007dcb9ded7f49c4dc8e3eed4a446712eb6fdf08a665a4f2352d6d2f8bdf17" - MultiImageManifest = `{"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"}}]}` - MultiImageDescriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` - LinuxAMD64ImageManifest = `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:2ef548696ac7dd66ef38aab5cc8fc5cc1fb637dfaedb3a9afc89bf16db9277e1","size":10240,"annotations":{"org.opencontainers.image.title":"hello.tar"}}]}` - LinuxAMD64ImageDescriptor = `{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458}` - LinuxAMD64ImageIndexDescriptor = `{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458,"platform":{"architecture":"amd64","os":"linux"}}` - LinuxAMD64ImageDigest = "sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1" - LinuxAMD64ImageConfig = "{\r\n \"architecture\": \"amd64\",\r\n \"os\": \"linux\"\r\n}" - LinuxAMD64ImageConfigDescriptor = `{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53}` + PreviewDesc = "** This command is in preview and under development. **" + ExampleDesc = "\nExample - " + ImageRepo = "command/images" + ArtifactRepo = "command/artifacts" + Repo = "command/images" + Namespace = "command" + FoobarImageTag = "foobar" + FoobarConfigDesc = "{\"mediaType\":\"application/vnd.unknown.config.v1+json\",\"digest\":\"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a\",\"size\":2}" + MultiImageTag = "multi" + MultiImageDigest = "sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f" + FoobarImageDigest = "sha256:fd6ed2f36b5465244d5dc86cb4e7df0ab8a9d24adc57825099f522fe009a22bb" + SignatureImageReferrerDigest = "sha256:0e007dcb9ded7f49c4dc8e3eed4a446712eb6fdf08a665a4f2352d6d2f8bdf17" + SBOMImageReferrerDigest = "sha256:32b78bd00723cd7d5251d4586f84d252530b7b5fe1c4104532767e6da4e04e47" + FallbackSignatureImageReferrerDigest = "sha256:8b3f7e000c4a6d32cd6bfcabfe874ed470d470501a09adc65afaf1c342f988ff" + FallbackSBOMImageReferrerDigest = "sha256:316405db72cc8f0212c19db23b498f9af8a456c9cd288f9e33acd1ba9e7cd534" + MultiImageManifest = `{"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"}}]}` + MultiImageDescriptor = `{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e2bfc9cc6a84ec2d7365b5a28c6bc5806b7fa581c9ad7883be955a64e3cc034f","size":706}` + LinuxAMD64ImageManifest = `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:2ef548696ac7dd66ef38aab5cc8fc5cc1fb637dfaedb3a9afc89bf16db9277e1","size":10240,"annotations":{"org.opencontainers.image.title":"hello.tar"}}]}` + LinuxAMD64ImageDescriptor = `{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458}` + LinuxAMD64ImageIndexDescriptor = `{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1","size":458,"platform":{"architecture":"amd64","os":"linux"}}` + LinuxAMD64ImageDigest = "sha256:9d84a5716c66a1d1b9c13f8ed157ba7d1edfe7f9b8766728b8a1f25c0d9c14c1" + LinuxAMD64ImageConfig = "{\r\n \"architecture\": \"amd64\",\r\n \"os\": \"linux\"\r\n}" + LinuxAMD64ImageConfigDescriptor = `{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:fe9dbc99451d0517d65e048c309f0b5afb2cc513b7a3d456b6cc29fe641386c5","size":53}` ) diff --git a/test/e2e/suite/command/cp.go b/test/e2e/suite/command/cp.go index ec0d84842..c2d31a744 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -63,14 +63,18 @@ var ( {Digest: "8d7a27ff2662", Name: "application/vnd.oci.artifact.manifest.v1+json"}, {Digest: "2dbea575a349", Name: "application/vnd.oci.artifact.manifest.v1+json"}, } - foobarFallbackReferrersStates = []match.StateKey{ + foobarImageReferrersStates = []match.StateKey{ {Digest: "0e007dcb9ded", Name: "application/vnd.oci.image.manifest.v1+json"}, {Digest: "32b78bd00723", Name: "application/vnd.oci.image.manifest.v1+json"}, } - foobarFallbackConfigStates = []match.StateKey{ + foobarImageConfigStates = []match.StateKey{ {Digest: "44136fa355b3", Name: "test.signature.file"}, {Digest: "44136fa355b3", Name: "test.sbom.file"}, } + foobarFallbackImageReferrersStates = []match.StateKey{ + {Digest: "316405db72cc", Name: "application/vnd.oci.image.manifest.v1+json"}, + {Digest: "8b3f7e000c4a", Name: "application/vnd.oci.image.manifest.v1+json"}, + } multiImageStates = []match.StateKey{ {Digest: "2ef548696ac7", Name: "hello.tar"}, {Digest: "fe9dbc99451d", Name: "application/vnd.oci.image.config.v1+json"}, @@ -107,7 +111,7 @@ var _ = Describe("Common registry users:", func() { }) It("should copy an image and its referrers to a new repository", func() { - stateKeys := append(append(foobarStates, foobarReferrersStates...), foobarFallbackConfigStates...) + stateKeys := append(append(foobarStates, foobarReferrersStates...), foobarImageConfigStates...) src := Reference(Host, ArtifactRepo, FoobarImageTag) dst := Reference(Host, cpTestRepo("referrers"), FoobarImageDigest) ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() @@ -149,24 +153,25 @@ var _ = Describe("OCI spec 1.0 registry users:", func() { dstManifest := ORAS("manifest", "fetch", dst).Exec().Out.Contents() gomega.Expect(srcManifest).To(gomega.Equal(dstManifest)) } - It("should copy an image artifact and its referrers to a fallback registry, and back to registry", func() { - repo := cpTestRepo("fallback") - stateKeys := append(append(foobarStates, foobarFallbackReferrersStates...), foobarFallbackConfigStates...) - src := Reference(Host, ArtifactRepo, FallbackSignatureArtifactDigest) + It("should copy an image artifact and its referrers from a registry to a fallback registry", func() { + repo := cpTestRepo("to-fallback") + stateKeys := append(append(foobarStates, foobarImageReferrersStates...), foobarImageConfigStates...) + src := Reference(Host, ArtifactRepo, SignatureImageReferrerDigest) dst := Reference(FallbackHost, repo, "") - dstWithDigest := Reference(FallbackHost, repo, FallbackSignatureArtifactDigest) ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() - validate(src, dstWithDigest) + validate(src, Reference(FallbackHost, repo, SignatureImageReferrerDigest)) ORAS("discover", "-o", "tree", Reference(FallbackHost, repo, FoobarImageDigest)). - WithDescription("discover referrer via subject").MatchKeyWords(FallbackSignatureArtifactDigest).Exec() - - src = dstWithDigest - dstWithDigest = Reference(Host, repo, FallbackSignatureArtifactDigest) - dst = Reference(Host, repo, "") + WithDescription("discover referrer via subject").MatchKeyWords(SignatureImageReferrerDigest, SBOMImageReferrerDigest).Exec() + }) + It("should copy an image artifact and its referrers from a fallback registry to a registry", func() { + repo := cpTestRepo("from-fallback") + stateKeys := append(append(foobarStates, foobarFallbackImageReferrersStates...), foobarImageConfigStates...) + src := Reference(FallbackHost, ArtifactRepo, FallbackSBOMImageReferrerDigest) + dst := Reference(Host, repo, "") ORAS("cp", "-r", src, dst, "-v").MatchStatus(stateKeys, true, len(stateKeys)).Exec() - validate(src, dstWithDigest) + validate(src, Reference(Host, repo, FallbackSBOMImageReferrerDigest)) ORAS("discover", "-o", "tree", Reference(Host, repo, FoobarImageDigest)). - WithDescription("discover referrer via subject").MatchKeyWords(FallbackSignatureArtifactDigest).Exec() + WithDescription("discover referrer via subject").MatchKeyWords(FallbackSignatureImageReferrerDigest, FallbackSBOMImageReferrerDigest).Exec() }) }) }) From ef06747f1fcbb0d04d636aec113b4326a9959a0e Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Fri, 10 Feb 2023 05:23:52 +0000 Subject: [PATCH 42/46] doc update Signed-off-by: Billy Zha --- test/e2e/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 830c1a5ec..8bc7b870d 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -14,11 +14,11 @@ PATH+=:$(go env GOPATH)/bin ``` ## Development -### 1. Using IDEs -Since E2E test suites are added as an nested module, the module file and checksum file are separated from oras CLI. To develop E2E tests, it's better to open your IDE or set the working diretory in `$REPO_ROOT/test/e2e/`. +### 1. Using IDE +Since E2E test suites are added as an nested module, the module file and checksum file are separated from oras CLI. To develop E2E tests, it's better to set the working directory to `$REPO_ROOT/test/e2e/` or open your IDE at it. ### 2. Testing pre-built ORAS Binary -By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. If you are only making changes to E2E test code, you may set `ORAS_PATH` towards your pre-built ORAS binary to speed up the testing process +By default, Gomega builds a temp binary every time before running e2e tests, which makes sure that latest code changes in the working directory are covered. If you are making changes to E2E test code only, set `ORAS_PATH` towards your pre-built ORAS binary to skip building and speed up the test. ### 3. Debugging via `go test` E2E specs can be ran natively without `ginkgo`: @@ -26,10 +26,10 @@ E2E specs can be ran natively without `ginkgo`: # run below command in the target test suite folder go test oras.land/oras/test/e2e/suite/${suite_name} ``` -This is is super handy when you want to step-by-step debugging from command-line or via an IDE. If you need to debug certain specs, use [focused specs](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. +This is super handy when you want to do step-by-step debugging from command-line or via an IDE. If you need to debug certain specs, use [focused specs](https://onsi.github.io/ginkgo/#focused-specs) but don't check it in. -### 4. Testing Different Distribution Services -The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former supports both image and artifact media types with referrer API; The latter only supports image media type with subject and provides referrers query via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). +### 4. Testing Registry Services +The backend of E2E tests are two registry services: [oras-distribution](https://github.com/oras-project/distribution) and [upstream distribution](https://github.com/distribution/distribution). The former is expected to support image and artifact media types and referrer API; The latter is expected to only support image media type with subject and provide referrers via [tag schema](https://github.com/opencontainers/distribution-spec/blob/v1.1.0-rc1/spec.md#referrers-tag-schema). You can run scenario test suite against your own registry services via setting `ORAS_REGISTRY_HOST` or `ORAS_REGISTRY_FALLBACK_HOST` environmental variables. @@ -41,10 +41,10 @@ ginkgo watch -r ``` ### 6. Trouble-shooting CLI -The executed commands should be shown in the ginkgo logs after `[It]`, with +The executed commands should be shown in the ginkgo logs after `[It]`, with full execution output in the E2E log. ### 7. Adding New Tests -Two suites will be maintained for E2E testing: +Three suites will be maintained for E2E testing: - command: contains test specs for single oras command execution - auth: contains test specs similar to command specs but specific to auth. It cannot be ran in parallel with command suite specs - scenario: contains featured scenarios with several oras commands execution @@ -61,9 +61,9 @@ Describe: ### 8. Adding New Test Data #### 8.1 Command Suite -Command suite uses pre-baked test data, which is a bunch of layered archive files compressed from distribution storage. Test data are all stored in `$REPO_ROOT/test/e2e/testdata/distribution/` but separated in different sub-folders: oras distribution uses `mount` and upstream distribution uses `mount_fallback`. +Command suite uses pre-baked test data, which is a bunch of layered archive files compressed from registry storage. Test data are all stored in `$REPO_ROOT/test/e2e/testdata/distribution/` but separated in different sub-folders: oras distribution uses `mount` and upstream distribution uses `mount_fallback`. -For both registries, the repository name should follow the convention of `command/$repo_suffix`. To add a new layer to the test data, compress the `docker` folder from the root directory of the distribution storage and copy it to `$REPO_ROOT/test/e2e/testdata/distribution/mount` folder. +For both registries, the repository name should follow the convention of `command/$repo_suffix`. To add a new layer to the test data, use the below command to compress the `docker` folder from the root directory of the registry storage and copy it to the corresponding subfolder in `$REPO_ROOT/test/e2e/testdata/distribution/mount`. ```shell tar -cvzf ${repo_suffix}.tar.gz --owner=0 --group=0 docker/ ``` From ca1b183d8fda1c08730b43a5427d9c5d1a431e6d Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Sun, 12 Feb 2023 21:17:28 +0800 Subject: [PATCH 43/46] fix error in doc Signed-off-by: Billy Zha --- test/e2e/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/README.md b/test/e2e/README.md index 8bc7b870d..c57dfee99 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -121,7 +121,7 @@ graph TD; A1-- foo2 -->A2(blob1) A1-- bar -->A3(blob2) - E1["test.sbom.file(image)"] -- subject --> C1 + E1["test.sbom.file(image)"] -- subject --> A1 E2["test.signature.file(image)"] -- subject --> E1 end end From a62f7ee47d97a4ea2e7e313b0c47d10dfdf73db9 Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 13 Feb 2023 12:48:12 +0800 Subject: [PATCH 44/46] fix typo 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 c2d31a744..ac3c3c1db 100644 --- a/test/e2e/suite/command/cp.go +++ b/test/e2e/suite/command/cp.go @@ -30,7 +30,7 @@ func cpTestRepo(text string) string { } var _ = Describe("ORAS beginners:", func() { - When("running repo command", func() { + When("running cp command", func() { RunAndShowPreviewInHelp([]string{"copy"}) It("should show preview and help doc", func() { From 65004e64a4dd53891ffabd14ddde9a6bda36a50c Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 13 Feb 2023 15:12:47 +0800 Subject: [PATCH 45/46] test(e2e): add e2e specs for `oras attach` Signed-off-by: Billy Zha --- .../testdata/foobar}/const.go | 24 +-- test/e2e/internal/utils/file.go | 7 + test/e2e/suite/command/attach.go | 178 ++++++++++++++++++ test/e2e/suite/command/manifest.go | 2 +- test/e2e/suite/scenario/oci_artifact.go | 31 +-- test/e2e/suite/scenario/oci_image.go | 5 +- 6 files changed, 217 insertions(+), 30 deletions(-) rename test/e2e/{suite/scenario => internal/testdata/foobar}/const.go (60%) create mode 100644 test/e2e/suite/command/attach.go diff --git a/test/e2e/suite/scenario/const.go b/test/e2e/internal/testdata/foobar/const.go similarity index 60% rename from test/e2e/suite/scenario/const.go rename to test/e2e/internal/testdata/foobar/const.go index bd65e1917..d347aa8af 100644 --- a/test/e2e/suite/scenario/const.go +++ b/test/e2e/internal/testdata/foobar/const.go @@ -13,30 +13,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -package scenario +package foobar import "oras.land/oras/test/e2e/internal/utils/match" var ( - blobFileNames = []string{ + BlobFileNames = []string{ "foobar/foo1", "foobar/foo2", "foobar/bar", } - pushFileStateKeys = []match.StateKey{ - {Digest: "2c26b46b68ff", Name: blobFileNames[0]}, - {Digest: "2c26b46b68ff", Name: blobFileNames[1]}, - {Digest: "fcde2b2edba5", Name: blobFileNames[2]}, + PushFileStateKeys = []match.StateKey{ + {Digest: "2c26b46b68ff", Name: BlobFileNames[0]}, + {Digest: "2c26b46b68ff", Name: BlobFileNames[1]}, + {Digest: "fcde2b2edba5", Name: BlobFileNames[2]}, } - configFileName = "foobar/config.json" - configFileStateKey = match.StateKey{ + ConfigFileName = "foobar/config.json" + ConfigFileStateKey = match.StateKey{ Digest: "46b68ac1696c", Name: "application/vnd.unknown.config.v1+json", } - attachFileName = "foobar/to-be-attached" - attachFileMedia = "test/oras.e2e" - attachFileStateKey = match.StateKey{ - Digest: "d3b29f7d12d9", Name: attachFileName, + AttachFileName = "foobar/to-be-attached" + AttachFileMedia = "test/oras.e2e" + AttachFileStateKey = match.StateKey{ + Digest: "d3b29f7d12d9", Name: AttachFileName, } ) diff --git a/test/e2e/internal/utils/file.go b/test/e2e/internal/utils/file.go index c5cd1b3ca..8a669721b 100644 --- a/test/e2e/internal/utils/file.go +++ b/test/e2e/internal/utils/file.go @@ -30,6 +30,13 @@ import ( var testFileRoot string +// CopyTestDataToTemp copies test data into the temp test folder. +func CopyTestDataToTemp() string { + tempDir := GinkgoT().TempDir() + Expect(CopyTestData(tempDir)).ShouldNot(HaveOccurred()) + return tempDir +} + // CopyTestData copies test data into the temp test folder. func CopyTestData(dstRoot string) error { return filepath.WalkDir(testFileRoot, func(path string, d fs.DirEntry, err error) error { diff --git a/test/e2e/suite/command/attach.go b/test/e2e/suite/command/attach.go new file mode 100644 index 000000000..f19b387d9 --- /dev/null +++ b/test/e2e/suite/command/attach.go @@ -0,0 +1,178 @@ +/* +Copyright The ORAS Authors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package command + +import ( + "encoding/json" + "fmt" + "path/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "oras.land/oras/test/e2e/internal/testdata/foobar" + . "oras.land/oras/test/e2e/internal/utils" + "oras.land/oras/test/e2e/internal/utils/match" +) + +func attachTestRepo(text string) string { + return fmt.Sprintf("command/attach/%d/%s", GinkgoRandomSeed(), text) +} + +var _ = Describe("ORAS beginners:", func() { + When("running attach command", func() { + RunAndShowPreviewInHelp([]string{"attach"}) + + It("should show preview and help doc", func() { + ORAS("attach", "--help").MatchKeyWords("[Preview] Attach", PreviewDesc, ExampleDesc).Exec() + }) + + It("should fail when no subject reference provided", func() { + ORAS("attach", "--artifact-type", "oras.test").ExpectFailure().MatchErrKeyWords("Error:").Exec() + }) + + It("should fail if no file reference or manifest annotation provided", func() { + ORAS("attach", "--artifact-type", "oras.test", Reference(Host, ImageRepo, FoobarImageTag)). + ExpectFailure().MatchErrKeyWords("Error: no blob or manifest annotation are provided").Exec() + }) + }) +}) + +var _ = Describe("Common registry users:", func() { + When("running attach command", func() { + It("should attach a file to a subject", func() { + testRepo := attachTestRepo("simple") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(Host, testRepo, FoobarImageTag) + prepare(Reference(Host, ImageRepo, FoobarImageTag), subjectRef) + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia)). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + }) + + It("should attach a file to a subject and export the built manifest", func() { + // prepare + testRepo := attachTestRepo("export-manifest") + tempDir := CopyTestDataToTemp() + exportName := "manifest.json" + subjectRef := Reference(Host, testRepo, FoobarImageTag) + prepare(Reference(Host, ImageRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--export-manifest", exportName). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + fetched := ORAS("manifest", "fetch", Reference(Host, testRepo, index.Manifests[0].Digest.String())).Exec().Out.Contents() + MatchFile(filepath.Join(tempDir, exportName), string(fetched), DefaultTimeout) + }) + It("should attach a file via a OCI Image", func() { + testRepo := attachTestRepo("image") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(Host, testRepo, FoobarImageTag) + prepare(Reference(Host, ImageRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--image-spec", "v1.1-image"). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest)) + }) + It("should attach a file via a OCI Artifact", func() { + testRepo := attachTestRepo("artifact") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(Host, testRepo, FoobarImageTag) + prepare(Reference(Host, ImageRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--image-spec", "v1.1-artifact"). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeArtifactManifest)) + }) + }) +}) + +var _ = Describe("Fallback registry users:", func() { + When("running attach command", func() { + It("should attach a file via a OCI Image", func() { + testRepo := attachTestRepo("fallback/image") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(FallbackHost, testRepo, FoobarImageTag) + prepare(Reference(FallbackHost, ArtifactRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--image-spec", "v1.1-image"). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest)) + }) + + It("should attach a file via a OCI Image by default", func() { + testRepo := attachTestRepo("fallback/default") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(FallbackHost, testRepo, FoobarImageTag) + prepare(Reference(FallbackHost, ArtifactRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--image-spec", "v1.1-image"). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest)) + }) + + It("should attach a file via a OCI Image and generate referrer via tag schema", func() { + testRepo := attachTestRepo("fallback/tag_schema") + tempDir := CopyTestDataToTemp() + subjectRef := Reference(FallbackHost, testRepo, FoobarImageTag) + prepare(Reference(FallbackHost, ArtifactRepo, FoobarImageTag), subjectRef) + // test + ORAS("attach", "--artifact-type", "test.attach", subjectRef, fmt.Sprintf("%s:%s", foobar.AttachFileName, foobar.AttachFileMedia), "--image-spec", "v1.1-image", "--distribution-spec", "v1.1-referrers-tag"). + WithWorkDir(tempDir). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, false, 1).Exec() + + // validate + var index ocispec.Index + bytes := ORAS("discover", subjectRef, "--distribution-spec", "v1.1-referrers-tag", "-o", "json").Exec().Out.Contents() + Expect(json.Unmarshal(bytes, &index)).ShouldNot(HaveOccurred()) + Expect(len(index.Manifests)).To(Equal(1)) + Expect(index.Manifests[0].MediaType).To(Equal(ocispec.MediaTypeImageManifest)) + }) + }) +}) diff --git a/test/e2e/suite/command/manifest.go b/test/e2e/suite/command/manifest.go index e6a0f9bc1..fb19e0d36 100644 --- a/test/e2e/suite/command/manifest.go +++ b/test/e2e/suite/command/manifest.go @@ -28,7 +28,7 @@ import ( ) func prepare(src string, dst string) { - ORAS("cp", src, dst).Exec() + ORAS("cp", src, dst).WithDescription("prepare test env").Exec() } func validate(repoRef string, tag string, gone bool) { diff --git a/test/e2e/suite/scenario/oci_artifact.go b/test/e2e/suite/scenario/oci_artifact.go index 067215637..5a0139222 100644 --- a/test/e2e/suite/scenario/oci_artifact.go +++ b/test/e2e/suite/scenario/oci_artifact.go @@ -20,6 +20,7 @@ import ( "path/filepath" . "github.com/onsi/ginkgo/v2" + "oras.land/oras/test/e2e/internal/testdata/foobar" . "oras.land/oras/test/e2e/internal/utils" "oras.land/oras/test/e2e/internal/utils/match" ) @@ -39,8 +40,8 @@ var _ = Describe("Common OCI artifact users:", Ordered, func() { pulledManifest := "packed.json" pullRoot := "pulled" It("should push and pull an artifact", func() { - ORAS("push", Reference(Host, repo, tag), "--artifact-type", "test-artifact", blobFileNames[0], blobFileNames[1], blobFileNames[2], "-v", "--export-manifest", pulledManifest). - MatchStatus(pushFileStateKeys, true, 3). + ORAS("push", Reference(Host, repo, tag), "--artifact-type", "test-artifact", foobar.BlobFileNames[0], foobar.BlobFileNames[1], foobar.BlobFileNames[2], "-v", "--export-manifest", pulledManifest). + MatchStatus(foobar.PushFileStateKeys, true, 3). WithWorkDir(tempDir). WithDescription("push with manifest exported").Exec() @@ -48,11 +49,11 @@ var _ = Describe("Common OCI artifact users:", Ordered, func() { MatchFile(filepath.Join(tempDir, pulledManifest), string(fetched.Out.Contents()), DefaultTimeout) ORAS("pull", Reference(Host, repo, tag), "-v", "-o", pullRoot). - MatchStatus(pushFileStateKeys, true, 3). + MatchStatus(foobar.PushFileStateKeys, true, 3). WithWorkDir(tempDir). WithDescription("pull artFiles with config").Exec() - for _, f := range blobFileNames { + for _, f := range foobar.BlobFileNames { Binary("diff", filepath.Join(f), filepath.Join(pullRoot, f)). WithWorkDir(tempDir). WithDescription("download identical file " + f).Exec() @@ -61,40 +62,40 @@ var _ = Describe("Common OCI artifact users:", Ordered, func() { It("should attach and pull an artifact", func() { subject := Reference(Host, repo, tag) - ORAS("attach", subject, "--artifact-type", "test.artifact1", fmt.Sprint(attachFileName, ":", attachFileMedia), "-v", "--export-manifest", pulledManifest). - MatchStatus([]match.StateKey{attachFileStateKey}, true, 1). + ORAS("attach", subject, "--artifact-type", "test.artifact1", fmt.Sprint(foobar.AttachFileName, ":", foobar.AttachFileMedia), "-v", "--export-manifest", pulledManifest). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, true, 1). WithWorkDir(tempDir). WithDescription("attach with manifest exported").Exec() session := ORAS("discover", subject, "-o", "json").Exec() digest := string(Binary("jq", "-r", ".manifests[].digest").WithInput(session.Out).Exec().Out.Contents()) - fetched := ORAS("manifest", "fetch", Reference(Host, repo, digest)).MatchKeyWords(attachFileMedia).Exec() + fetched := ORAS("manifest", "fetch", Reference(Host, repo, digest)).MatchKeyWords(foobar.AttachFileMedia).Exec() MatchFile(filepath.Join(tempDir, pulledManifest), string(fetched.Out.Contents()), DefaultTimeout) ORAS("pull", Reference(Host, repo, digest), "-v", "-o", pullRoot). - MatchStatus([]match.StateKey{attachFileStateKey}, true, 1). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, true, 1). WithWorkDir(tempDir). WithDescription("pull attached artifact").Exec() - Binary("diff", filepath.Join(attachFileName), filepath.Join(pullRoot, attachFileName)). + Binary("diff", filepath.Join(foobar.AttachFileName), filepath.Join(pullRoot, foobar.AttachFileName)). WithWorkDir(tempDir). - WithDescription("download identical file " + attachFileName).Exec() + WithDescription("download identical file " + foobar.AttachFileName).Exec() - ORAS("attach", subject, "--artifact-type", "test.artifact2", fmt.Sprint(attachFileName, ":", attachFileMedia), "-v", "--export-manifest", pulledManifest). - MatchStatus([]match.StateKey{attachFileStateKey}, true, 1). + ORAS("attach", subject, "--artifact-type", "test.artifact2", fmt.Sprint(foobar.AttachFileName, ":", foobar.AttachFileMedia), "-v", "--export-manifest", pulledManifest). + MatchStatus([]match.StateKey{foobar.AttachFileStateKey}, true, 1). WithWorkDir(tempDir). WithDescription("attach again with manifest exported").Exec() session = ORAS("discover", subject, "-o", "json", "--artifact-type", "test.artifact2").Exec() digest = string(Binary("jq", "-r", ".manifests[].digest").WithInput(session.Out).Exec().Out.Contents()) - fetched = ORAS("manifest", "fetch", Reference(Host, repo, digest)).MatchKeyWords(attachFileMedia).Exec() + fetched = ORAS("manifest", "fetch", Reference(Host, repo, digest)).MatchKeyWords(foobar.AttachFileMedia).Exec() MatchFile(filepath.Join(tempDir, pulledManifest), string(fetched.Out.Contents()), DefaultTimeout) ORAS("pull", Reference(Host, repo, string(digest)), "-v", "-o", pullRoot, "--include-subject"). - MatchStatus(append(pushFileStateKeys, attachFileStateKey), true, 4). + MatchStatus(append(foobar.PushFileStateKeys, foobar.AttachFileStateKey), true, 4). WithWorkDir(tempDir). WithDescription("pull attached artifact and subject").Exec() - for _, f := range append(blobFileNames, attachFileName) { + for _, f := range append(foobar.BlobFileNames, foobar.AttachFileName) { Binary("diff", filepath.Join(f), filepath.Join(pullRoot, f)). WithWorkDir(tempDir). WithDescription("download identical file " + f).Exec() diff --git a/test/e2e/suite/scenario/oci_image.go b/test/e2e/suite/scenario/oci_image.go index f34199b0f..26999cb25 100644 --- a/test/e2e/suite/scenario/oci_image.go +++ b/test/e2e/suite/scenario/oci_image.go @@ -19,13 +19,14 @@ import ( "path/filepath" . "github.com/onsi/ginkgo/v2" + "oras.land/oras/test/e2e/internal/testdata/foobar" . "oras.land/oras/test/e2e/internal/utils" ) var _ = Describe("OCI image user:", Ordered, func() { repo := "scenario/oci-image" - files := append([]string{configFileName}, blobFileNames...) - statusKeys := append(pushFileStateKeys, configFileStateKey) + files := append([]string{foobar.ConfigFileName}, foobar.BlobFileNames...) + statusKeys := append(foobar.PushFileStateKeys, foobar.ConfigFileStateKey) When("pushing images and check", func() { tag := "image" var tempDir string From 199821014910bc0e4880fe1a16d9d1d48525042b Mon Sep 17 00:00:00 2001 From: Billy Zha Date: Mon, 13 Feb 2023 15:58:31 +0800 Subject: [PATCH 46/46] code clean Signed-off-by: Billy Zha --- test/e2e/internal/utils/file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/internal/utils/file.go b/test/e2e/internal/utils/file.go index 8a669721b..6cdd8f4a6 100644 --- a/test/e2e/internal/utils/file.go +++ b/test/e2e/internal/utils/file.go @@ -30,14 +30,14 @@ import ( var testFileRoot string -// CopyTestDataToTemp copies test data into the temp test folder. +// CopyTestDataToTemp copies test data into a temp folder and return it. func CopyTestDataToTemp() string { tempDir := GinkgoT().TempDir() Expect(CopyTestData(tempDir)).ShouldNot(HaveOccurred()) return tempDir } -// CopyTestData copies test data into the temp test folder. +// CopyTestData copies test data into dstRoot. func CopyTestData(dstRoot string) error { return filepath.WalkDir(testFileRoot, func(path string, d fs.DirEntry, err error) error { if err != nil {