diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index d9c0d301..d811e54e 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -2121,15 +2121,10 @@ func TestGroupsPermissionsForLDAP(t *testing.T) { cm.StartAndWait(port) defer cm.StopServer() - cfg, layers, manifest, err := test.GetImageComponents(10000) - So(err, ShouldBeNil) + img := test.CreateDefaultImage() - err = test.UploadImageWithBasicAuth( - test.Image{ - Config: cfg, - Layers: layers, - Manifest: manifest, - }, baseURL, repo, + err = test.UploadImageWithBasicAuthRef( + img, baseURL, repo, img.DigestStr(), username, passphrase) So(err, ShouldBeNil) diff --git a/pkg/cli/image_cmd_test.go b/pkg/cli/image_cmd_test.go index 4db221ef..70fa6848 100644 --- a/pkg/cli/image_cmd_test.go +++ b/pkg/cli/image_cmd_test.go @@ -1503,13 +1503,13 @@ func runDisplayIndexTests(baseURL string) { actual := strings.TrimSpace(str) // Actual cli output should be something similar to (order of images may differ): // REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE - // repo multi-arch * 0f844b3e false 1.5kB - // linux/amd64 2ab1a275 false 634B - // windows/arm64/v6 55fdd23a false 444B + // repo multi-arch * 28665f71 false 1.5kB + // linux/amd64 02e0ac42 false 644B + // windows/arm64/v6 5e09b7f9 false 444B So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") - So(actual, ShouldContainSubstring, "repo multi-arch * 0f844b3e false 1.5kB ") - So(actual, ShouldContainSubstring, "linux/amd64 2ab1a275 false 634B ") - So(actual, ShouldContainSubstring, "windows/arm64/v6 55fdd23a false 501B") + So(actual, ShouldContainSubstring, "repo multi-arch * 28665f71 false 1.5kB ") + So(actual, ShouldContainSubstring, "linux/amd64 02e0ac42 false 644B ") + So(actual, ShouldContainSubstring, "windows/arm64/v6 5e09b7f9 false 506B") }) Convey("Test Image Index Verbose", func() { @@ -1531,18 +1531,18 @@ func runDisplayIndexTests(baseURL string) { actual := strings.TrimSpace(str) // Actual cli output should be something similar to (order of images may differ): // REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE - // repo multi-arch * 0f844b3e false 1.5kB - // linux/amd64 2ab1a275 58cc9abe false 634B + // repo multi-arch * 28665f71 false 1.5kB + // linux/amd64 02e0ac42 58cc9abe false 644B // cbb5b121 4B // a00291e8 4B - // windows/arm64/v6 55fdd23a 5132a1cd false 501B + // windows/arm64/v6 5e09b7f9 5132a1cd false 506B // 7d08ce29 4B So(actual, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST CONFIG SIGNED LAYERS SIZE") - So(actual, ShouldContainSubstring, "repo multi-arch * 0f844b3e false 1.5kB") - So(actual, ShouldContainSubstring, "linux/amd64 2ab1a275 58cc9abe false 634B") + So(actual, ShouldContainSubstring, "repo multi-arch * 28665f71 false 1.5kB") + So(actual, ShouldContainSubstring, "linux/amd64 02e0ac42 58cc9abe false 644B") So(actual, ShouldContainSubstring, "cbb5b121 4B") So(actual, ShouldContainSubstring, "a00291e8 4B") - So(actual, ShouldContainSubstring, "windows/arm64/v6 55fdd23a 5132a1cd false 501B") + So(actual, ShouldContainSubstring, "windows/arm64/v6 5e09b7f9 5132a1cd false 506B") So(actual, ShouldContainSubstring, "7d08ce29 4B") }) } @@ -1552,42 +1552,35 @@ func uploadTestMultiarch(baseURL string) { layer11 := []byte{11, 12, 13, 14} layer12 := []byte{16, 17, 18, 19} - image1, err := test.GetImageWithComponents( - ispec.Image{ - Platform: ispec.Platform{ - OS: "linux", - Architecture: "amd64", - }, - }, - [][]byte{ + image1 := test.CreateImageWith(). + LayerBlobs([][]byte{ layer11, layer12, - }, - ) - So(err, ShouldBeNil) + }). + ImageConfig( + ispec.Image{ + Platform: ispec.Platform{OS: "linux", Architecture: "amd64"}, + }, + ).Build() // ------ Define Image2 layer21 := []byte{21, 22, 23, 24} - image2, err := test.GetImageWithComponents( - ispec.Image{ - Platform: ispec.Platform{ - OS: "windows", - Architecture: "arm64", - Variant: "v6", - }, - }, - [][]byte{ + image2 := test.CreateImageWith(). + LayerBlobs([][]byte{ layer21, - }, - ) - So(err, ShouldBeNil) + }). + ImageConfig( + ispec.Image{ + Platform: ispec.Platform{OS: "windows", Architecture: "arm64", Variant: "v6"}, + }, + ).Build() // ------- Upload The multiarch image - multiarch := test.GetMultiarchImageForImages("multi-arch", []test.Image{image1, image2}) + multiarch := test.GetMultiarchImageForImages([]test.Image{image1, image2}) - err = test.UploadMultiarchImage(multiarch, baseURL, "repo") + err := test.UploadMultiarchImageWithRef(multiarch, baseURL, "repo", "multi-arch") So(err, ShouldBeNil) } diff --git a/pkg/cli/search_cmd_referrers_test.go b/pkg/cli/search_cmd_referrers_test.go index 09bed5da..aa3cdcc6 100644 --- a/pkg/cli/search_cmd_referrers_test.go +++ b/pkg/cli/search_cmd_referrers_test.go @@ -12,7 +12,6 @@ import ( "strings" "testing" - ispec "github.com/opencontainers/image-spec/specs-go/v1" . "github.com/smartystreets/goconvey/convey" "zotregistry.io/zot/pkg/api" @@ -205,36 +204,26 @@ func TestReferrerCLI(t *testing.T) { defer cm.StopServer() repo := repoName - image, err := test.GetRandomImage("tag") - So(err, ShouldBeNil) - imgDigest, err := image.Digest() + image := test.CreateRandomImage() + + err := test.UploadImageWithRef(image, baseURL, repo, "tag") So(err, ShouldBeNil) - err = test.UploadImage(image, baseURL, repo) - So(err, ShouldBeNil) + ref1 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + Subject(image.DescriptorRef()).Build() - // add referrers - ref1, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref1.Reference = "" + ref2 := test.CreateImageWith(). + RandomLayers(1, 10). + ArtifactConfig(customArtTypeV1). + Subject(image.DescriptorRef()).Build() - ref1Digest, err := ref1.Digest() - So(err, ShouldBeNil) - - ref2, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref2.Reference = "" - ref2.Manifest.Config.MediaType = customArtTypeV1 - ref2Digest, err := ref2.Digest() - So(err, ShouldBeNil) - - ref3, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref3.Manifest.ArtifactType = customArtTypeV2 - ref3.Manifest.Config = ispec.DescriptorEmptyJSON - ref3.Reference = "" - ref3Digest, err := ref3.Digest() - So(err, ShouldBeNil) + ref3 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + ArtifactType(customArtTypeV2). + Subject(image.DescriptorRef()).Build() err = test.UploadImage(ref1, baseURL, repo) So(err, ShouldBeNil) @@ -245,7 +234,7 @@ func TestReferrerCLI(t *testing.T) { err = test.UploadImage(ref3, baseURL, repo) So(err, ShouldBeNil) - args := []string{"reftest", "--subject", repo + "@" + imgDigest.String()} + args := []string{"reftest", "--subject", repo + "@" + image.DigestStr()} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`, baseURL)) @@ -262,9 +251,9 @@ func TestReferrerCLI(t *testing.T) { space := regexp.MustCompile(`\s+`) str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST") - So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 557 B "+ref1Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v1 547 B "+ref2Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v2 610 B "+ref3Digest.String()) + So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr()) fmt.Println(buff.String()) @@ -286,9 +275,9 @@ func TestReferrerCLI(t *testing.T) { So(err, ShouldBeNil) str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST") - So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 557 B "+ref1Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v1 547 B "+ref2Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v2 610 B "+ref3Digest.String()) + So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr()) fmt.Println(buff.String()) }) @@ -312,48 +301,38 @@ func TestReferrerCLI(t *testing.T) { defer cm.StopServer() repo := repoName - image, err := test.GetRandomImage("tag") - So(err, ShouldBeNil) - imgDigest, err := image.Digest() + image := test.CreateRandomImage() + + err := test.UploadImageWithRef(image, baseURL, repo, "tag") So(err, ShouldBeNil) - err = test.UploadImage(image, baseURL, repo) - So(err, ShouldBeNil) + ref1 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + Subject(image.DescriptorRef()).Build() - // add referrers - ref1, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref1Digest, err := ref1.Digest() - So(err, ShouldBeNil) + ref2 := test.CreateImageWith(). + RandomLayers(1, 10). + ArtifactConfig(customArtTypeV1). + Subject(image.DescriptorRef()).Build() - ref2, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref2.Manifest.Config.MediaType = customArtTypeV1 - ref2Digest, err := ref2.Digest() - So(err, ShouldBeNil) + ref3 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + ArtifactType(customArtTypeV2). + Subject(image.DescriptorRef()).Build() - ref3, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref3.Manifest.ArtifactType = customArtTypeV2 - ref3.Manifest.Config = ispec.DescriptorEmptyJSON - - ref3Digest, err := ref3.Digest() - So(err, ShouldBeNil) - - ref1.Reference = "" err = test.UploadImage(ref1, baseURL, repo) So(err, ShouldBeNil) - ref2.Reference = "" err = test.UploadImage(ref2, baseURL, repo) So(err, ShouldBeNil) - ref3.Reference = "" err = test.UploadImage(ref3, baseURL, repo) So(err, ShouldBeNil) // get referrers by digest - args := []string{"reftest", "--subject", repo + "@" + imgDigest.String()} + args := []string{"reftest", "--subject", repo + "@" + image.DigestStr()} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`, baseURL)) @@ -369,9 +348,9 @@ func TestReferrerCLI(t *testing.T) { space := regexp.MustCompile(`\s+`) str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST") - So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 557 B "+ref1Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v1 547 B "+ref2Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v2 610 B "+ref3Digest.String()) + So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr()) fmt.Println(buff.String()) os.Remove(configPath) @@ -390,9 +369,9 @@ func TestReferrerCLI(t *testing.T) { So(err, ShouldBeNil) str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "ARTIFACT TYPE SIZE DIGEST") - So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 557 B "+ref1Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v1 547 B "+ref2Digest.String()) - So(str, ShouldContainSubstring, "application/custom.art.type.v2 610 B "+ref3Digest.String()) + So(str, ShouldContainSubstring, "application/vnd.oci.image.config.v1+json 563 B "+ref1.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v1 551 B "+ref2.DigestStr()) + So(str, ShouldContainSubstring, "custom.art.type.v2 611 B "+ref3.DigestStr()) fmt.Println(buff.String()) }) } @@ -417,41 +396,39 @@ func TestFormatsReferrersCLI(t *testing.T) { defer cm.StopServer() repo := repoName - image, err := test.GetRandomImage("tag") - So(err, ShouldBeNil) - imgDigest, err := image.Digest() - So(err, ShouldBeNil) + image := test.CreateRandomImage() - err = test.UploadImage(image, baseURL, repo) + err := test.UploadImageWithRef(image, baseURL, repo, "tag") So(err, ShouldBeNil) // add referrers - ref1, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) + ref1 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + Subject(image.DescriptorRef()).Build() - ref2, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref2.Manifest.Config.MediaType = customArtTypeV1 + ref2 := test.CreateImageWith(). + RandomLayers(1, 10). + ArtifactConfig(customArtTypeV1). + Subject(image.DescriptorRef()).Build() - ref3, err := test.GetImageWithSubject(imgDigest, ispec.MediaTypeImageManifest) - So(err, ShouldBeNil) - ref3.Manifest.ArtifactType = customArtTypeV2 - ref3.Manifest.Config = ispec.DescriptorEmptyJSON + ref3 := test.CreateImageWith(). + RandomLayers(1, 10). + RandomConfig(). + ArtifactType(customArtTypeV2). + Subject(image.DescriptorRef()).Build() - ref1.Reference = "" err = test.UploadImage(ref1, baseURL, repo) So(err, ShouldBeNil) - ref2.Reference = "" err = test.UploadImage(ref2, baseURL, repo) So(err, ShouldBeNil) - ref3.Reference = "" err = test.UploadImage(ref3, baseURL, repo) So(err, ShouldBeNil) Convey("JSON format", func() { - args := []string{"reftest", "--output", "json", "--subject", repo + "@" + imgDigest.String()} + args := []string{"reftest", "--output", "json", "--subject", repo + "@" + image.DigestStr()} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`, baseURL)) @@ -469,7 +446,7 @@ func TestFormatsReferrersCLI(t *testing.T) { fmt.Println(buff.String()) }) Convey("YAML format", func() { - args := []string{"reftest", "--output", "yaml", "--subject", repo + "@" + imgDigest.String()} + args := []string{"reftest", "--output", "yaml", "--subject", repo + "@" + image.DigestStr()} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`, baseURL)) @@ -487,7 +464,7 @@ func TestFormatsReferrersCLI(t *testing.T) { fmt.Println(buff.String()) }) Convey("Invalid format", func() { - args := []string{"reftest", "--output", "invalid_format", "--subject", repo + "@" + imgDigest.String()} + args := []string{"reftest", "--output", "invalid_format", "--subject", repo + "@" + image.DigestStr()} configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"reftest","url":"%s","showspinner":false}]}`, baseURL)) diff --git a/pkg/cli/search_cmd_test.go b/pkg/cli/search_cmd_test.go index b580f9f9..57cb9643 100644 --- a/pkg/cli/search_cmd_test.go +++ b/pkg/cli/search_cmd_test.go @@ -105,26 +105,24 @@ func TestSearchCLI(t *testing.T) { r3tag2 = "repo3tag2" ) - image1, err := test.GetImageWithConfig(ispec.Image{ - Platform: ispec.Platform{ - OS: "Os", - Architecture: "Arch", - }, - }) - So(err, ShouldBeNil) - img1Digest, err := image1.Digest() - formatterDigest1 := img1Digest.Encoded()[:8] - So(err, ShouldBeNil) + image1 := test.CreateImageWith(). + RandomLayers(1, 10). + ImageConfig(ispec.Image{ + Created: test.DefaultTimeRef(), + Platform: ispec.Platform{OS: "Os", Architecture: "Arch"}, + }). + Build() + formatterDigest1 := image1.Digest().Encoded()[:8] - image2, err := test.GetRandomImage("") - So(err, ShouldBeNil) - img2Digest, err := image2.Digest() - formatterDigest2 := img2Digest.Encoded()[:8] - So(err, ShouldBeNil) + image2 := test.CreateImageWith(). + RandomLayers(1, 10). + DefaultConfig(). + Build() + formatterDigest2 := image2.Digest().Encoded()[:8] // repo1 image1.Reference = r1tag1 - err = test.UploadImage(image1, baseURL, repo1) + err := test.UploadImage(image1, baseURL, repo1) So(err, ShouldBeNil) image2.Reference = r1tag2 @@ -168,7 +166,7 @@ func TestSearchCLI(t *testing.T) { space := regexp.MustCompile(`\s+`) str := strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "NAME SIZE LAST UPDATED DOWNLOADS STARS PLATFORMS") - So(str, ShouldContainSubstring, "repo/test/alpine 1.1kB 0001-01-01 00:00:00 +0000 UTC 0 0") + So(str, ShouldContainSubstring, "repo/test/alpine 1.1kB 2010-01-01 01:01:01 +0000 UTC 0 0") So(str, ShouldContainSubstring, "Os/Arch") So(str, ShouldContainSubstring, "linux/amd64") @@ -193,8 +191,8 @@ func TestSearchCLI(t *testing.T) { So(err, ShouldBeNil) str = strings.TrimSpace(space.ReplaceAllString(buff.String(), " ")) So(str, ShouldContainSubstring, "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE") - So(str, ShouldContainSubstring, "repo/alpine repo2tag1 Os/Arch "+formatterDigest1+" false 577B") - So(str, ShouldContainSubstring, "repo/alpine repo2tag2 linux/amd64 "+formatterDigest2+" false 524B") + So(str, ShouldContainSubstring, "repo/alpine repo2tag1 Os/Arch "+formatterDigest1+" false 525B") + So(str, ShouldContainSubstring, "repo/alpine repo2tag2 linux/amd64 "+formatterDigest2+" false 552B") fmt.Println("\n", buff.String()) }) @@ -233,15 +231,13 @@ func TestFormatsSearchCLI(t *testing.T) { r3tag2 = "repo3tag2" ) - image1, err := test.GetRandomImage("") - So(err, ShouldBeNil) + image1 := test.CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build() - image2, err := test.GetRandomImage("") - So(err, ShouldBeNil) + image2 := test.CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build() // repo1 image1.Reference = r1tag1 - err = test.UploadImage(image1, baseURL, repo1) + err := test.UploadImage(image1, baseURL, repo1) So(err, ShouldBeNil) image2.Reference = r1tag2 diff --git a/pkg/extensions/search/cve/cve_test.go b/pkg/extensions/search/cve/cve_test.go index b2c6aec9..b31a07b7 100644 --- a/pkg/extensions/search/cve/cve_test.go +++ b/pkg/extensions/search/cve/cve_test.go @@ -1568,8 +1568,7 @@ func TestFixedTagsWithIndex(t *testing.T) { Platform: ispec.Platform{OS: "linux", Architecture: "amd64"}, }) So(err, ShouldBeNil) - vulnDigest, err := vulnManifest.Digest() - So(err, ShouldBeNil) + vulnDigest := vulnManifest.Digest() fixedManifestCreated := time.Date(2010, 1, 1, 1, 1, 1, 1, time.UTC) fixedManifest, err := GetImageWithConfig(ispec.Image{ @@ -1577,14 +1576,12 @@ func TestFixedTagsWithIndex(t *testing.T) { Platform: ispec.Platform{OS: "windows", Architecture: "amd64"}, }) So(err, ShouldBeNil) - fixedDigest, err := fixedManifest.Digest() - So(err, ShouldBeNil) + fixedDigest := fixedManifest.Digest() - multiArch := GetMultiarchImageForImages("multi-arch-tag", []Image{fixedManifest, vulnManifest}) - multiArchDigest, err := multiArch.Digest() - So(err, ShouldBeNil) + multiArch := GetMultiarchImageForImages([]Image{fixedManifest, vulnManifest}) + multiArchDigest := multiArch.Digest() - err = UploadMultiarchImage(multiArch, baseURL, "repo") + err = UploadMultiarchImageWithRef(multiArch, baseURL, "repo", "multi-arch-tag") So(err, ShouldBeNil) // oldest vulnerability diff --git a/pkg/extensions/search/cve/trivy/scanner_test.go b/pkg/extensions/search/cve/trivy/scanner_test.go index 8da74e70..435fa452 100644 --- a/pkg/extensions/search/cve/trivy/scanner_test.go +++ b/pkg/extensions/search/cve/trivy/scanner_test.go @@ -46,21 +46,13 @@ func TestScanningByDigest(t *testing.T) { cm.StartAndWait(port) defer cm.StopServer() // push index with 2 manifests: one with vulns and one without - vulnImage, err := test.GetVulnImage("") - So(err, ShouldBeNil) - vulnDigest, err := vulnImage.Digest() - So(err, ShouldBeNil) + vulnImage := test.CreateVulnerableImage() - simpleImage, err := test.GetRandomImage("") - So(err, ShouldBeNil) - simpleDigest, err := simpleImage.Digest() - So(err, ShouldBeNil) + simpleImage := test.CreateRandomImage() - multiArch := test.GetMultiarchImageForImages("multi-arch-tag", []test.Image{simpleImage, vulnImage}) - multiArchDigest, err := multiArch.Digest() - So(err, ShouldBeNil) + multiArch := test.GetMultiarchImageForImages([]test.Image{simpleImage, vulnImage}) - err = test.UploadMultiarchImage(multiArch, baseURL, "multi-arch") + err := test.UploadMultiarchImageWithRef(multiArch, baseURL, "multi-arch", "multi-arch-tag") So(err, ShouldBeNil) // scan @@ -69,17 +61,17 @@ func TestScanningByDigest(t *testing.T) { err = scanner.UpdateDB() So(err, ShouldBeNil) - cveMap, err := scanner.ScanImage("multi-arch@" + vulnDigest.String()) + cveMap, err := scanner.ScanImage("multi-arch@" + vulnImage.DigestStr()) So(err, ShouldBeNil) So(cveMap, ShouldContainKey, test.Vulnerability1ID) So(cveMap, ShouldContainKey, test.Vulnerability2ID) So(cveMap, ShouldContainKey, test.Vulnerability3ID) - cveMap, err = scanner.ScanImage("multi-arch@" + simpleDigest.String()) + cveMap, err = scanner.ScanImage("multi-arch@" + simpleImage.DigestStr()) So(err, ShouldBeNil) So(cveMap, ShouldBeEmpty) - cveMap, err = scanner.ScanImage("multi-arch@" + multiArchDigest.String()) + cveMap, err = scanner.ScanImage("multi-arch@" + multiArch.DigestStr()) So(err, ShouldBeNil) So(cveMap, ShouldContainKey, test.Vulnerability1ID) So(cveMap, ShouldContainKey, test.Vulnerability2ID) @@ -120,7 +112,7 @@ func TestScannerErrors(t *testing.T) { func TestVulnerableLayer(t *testing.T) { Convey("Vulnerable layer", t, func() { - vulnerableLayer, err := test.GetLayerWithVulnerability(1) + vulnerableLayer, err := test.GetLayerWithVulnerability() So(err, ShouldBeNil) created, err := time.Parse(time.RFC3339, "2023-03-29T18:19:24Z") @@ -142,15 +134,10 @@ func TestVulnerableLayer(t *testing.T) { }, } - img, err := test.GetImageWithComponents( - config, - [][]byte{ - vulnerableLayer, - }, - ) - So(err, ShouldBeNil) - imgDigest, err := img.Digest() - So(err, ShouldBeNil) + img := test.CreateImageWith(). + LayerBlobs([][]byte{vulnerableLayer}). + ImageConfig(config). + Build() tempDir := t.TempDir() @@ -182,7 +169,7 @@ func TestVulnerableLayer(t *testing.T) { err = scanner.UpdateDB() So(err, ShouldBeNil) - cveMap, err := scanner.ScanImage("repo@" + imgDigest.String()) + cveMap, err := scanner.ScanImage("repo@" + img.DigestStr()) So(err, ShouldBeNil) t.Logf("cveMap: %v", cveMap) // As of July 15 2023 there are 3 CVEs: CVE-2023-1255, CVE-2023-2650, CVE-2023-2975 diff --git a/pkg/extensions/search/digest_test.go b/pkg/extensions/search/digest_test.go index acbeaf39..efc0537a 100644 --- a/pkg/extensions/search/digest_test.go +++ b/pkg/extensions/search/digest_test.go @@ -111,8 +111,7 @@ func TestDigestSearchHTTP(t *testing.T) { So(err, ShouldBeNil) image2.Reference = ver001 - manifestDigest, err := image2.Digest() - So(err, ShouldBeNil) + manifestDigest := image2.Digest() err = UploadImage(image2, baseURL, "zot-test") So(err, ShouldBeNil) diff --git a/pkg/extensions/search/search_test.go b/pkg/extensions/search/search_test.go index c757581e..788fd211 100644 --- a/pkg/extensions/search/search_test.go +++ b/pkg/extensions/search/search_test.go @@ -1112,8 +1112,7 @@ func TestGetReferrersGQL(t *testing.T) { targetImg, err := GetRandomImage("") So(err, ShouldBeNil) - targetDigest, err := targetImg.Digest() - So(err, ShouldBeNil) + targetDigest := targetImg.Digest() err = UploadImage(targetImg, baseURL, "repo") So(err, ShouldBeNil) @@ -1128,8 +1127,7 @@ func TestGetReferrersGQL(t *testing.T) { Digest: targetDigest, } - indexReferrerDigest, err := indexReferrer.Digest() - So(err, ShouldBeNil) + indexReferrerDigest := indexReferrer.Digest() err = UploadMultiarchImage(indexReferrer, baseURL, "repo") So(err, ShouldBeNil) @@ -1545,8 +1543,7 @@ func TestExpandedRepoInfo(t *testing.T) { image, err := GetRandomImage(test) So(err, ShouldBeNil) - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() err = UploadImage(image, baseURL, "repo") So(err, ShouldBeNil) @@ -1713,7 +1710,7 @@ func TestExpandedRepoInfo(t *testing.T) { }) So(err, ShouldBeNil) - multiImage1 := GetMultiarchImageForImages("1.0.0", []Image{indexSubImage11, indexSubImage12}) + multiImage1 := GetMultiarchImageForImages([]Image{indexSubImage11, indexSubImage12}) indexSubImage21, err := GetImageWithConfig(ispec.Image{ Platform: ispec.Platform{ @@ -1739,15 +1736,13 @@ func TestExpandedRepoInfo(t *testing.T) { }) So(err, ShouldBeNil) - multiImage2 := GetMultiarchImageForImages("2.0.0", - []Image{indexSubImage21, indexSubImage22, indexSubImage23}, - ) + multiImage2 := GetMultiarchImageForImages([]Image{indexSubImage21, indexSubImage22, indexSubImage23}) // ------- Write test Images - err = WriteMultiArchImageToFileSystem(multiImage1, "repo", storeController) + err = WriteMultiArchImageToFileSystem(multiImage1, "repo", "1.0.0", storeController) So(err, ShouldBeNil) - err = WriteMultiArchImageToFileSystem(multiImage2, "repo", storeController) + err = WriteMultiArchImageToFileSystem(multiImage2, "repo", "2.0.0", storeController) So(err, ShouldBeNil) // ------- Start Server /tmp/TestExpandedRepoInfo4021254039/005 @@ -4845,11 +4840,11 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - multiImage := GetMultiarchImageForImages("latest", []Image{ + multiImage := GetMultiarchImageForImages([]Image{ imageAMD64, imageSomeArch, }) - err = UploadMultiarchImage(multiImage, baseURL, "test-repo") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "test-repo", "latest") So(err, ShouldBeNil) // ---------------- BASE IMAGE ------------------- @@ -4868,11 +4863,9 @@ func RunMetaDBIndexTests(baseURL, port string) { ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-same-layers", []Image{ - image1, image2, - }) + multiImage = GetMultiarchImageForImages([]Image{image1, image2}) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-same-layers") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-same-layers", "index-one-arch-same-layers") So(err, ShouldBeNil) // ---------------- SAME LAYERS ------------------- @@ -4891,10 +4884,9 @@ func RunMetaDBIndexTests(baseURL, port string) { [][]byte{imageAMD64.Layers[0]}, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-less-layers", []Image{ - image1, image2, - }) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-less-layers") + multiImage = GetMultiarchImageForImages([]Image{image1, image2}) + + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-less-layers", "index-one-arch-less-layers") So(err, ShouldBeNil) // ---------------- LESS LAYERS ------------------- @@ -4915,10 +4907,9 @@ func RunMetaDBIndexTests(baseURL, port string) { [][]byte{auxLayer}, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-less-layers-false", []Image{ - image1, image2, - }) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-less-layers-false") + multiImage = GetMultiarchImageForImages([]Image{image1, image2}) + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-less-layers-false", + "index-one-arch-less-layers-false") So(err, ShouldBeNil) // ---------------- LESS LAYERS FALSE ------------------- @@ -4937,11 +4928,9 @@ func RunMetaDBIndexTests(baseURL, port string) { append(imageAMD64.Layers, []byte{1, 3, 55}), ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-more-layers", []Image{ - image1, image2, - }) + multiImage = GetMultiarchImageForImages([]Image{image1, image2}) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-more-layers") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-more-layers", "index-one-arch-more-layers") So(err, ShouldBeNil) // ---------------- MORE LAYERS ------------------- @@ -4988,8 +4977,7 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - baseLinuxAMD64Digest, err := imageAMD64.Digest() - So(err, ShouldBeNil) + baseLinuxAMD64Digest := imageAMD64.Digest() imageSomeArch, err := GetImageWithComponents( ispec.Image{ @@ -5003,14 +4991,10 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - baseLinuxSomeArchDigest, err := imageSomeArch.Digest() - So(err, ShouldBeNil) + baseLinuxSomeArchDigest := imageSomeArch.Digest() - multiImage := GetMultiarchImageForImages("index", []Image{ - imageAMD64, - imageSomeArch, - }) - err = UploadMultiarchImage(multiImage, baseURL, "test-repo") + multiImage := GetMultiarchImageForImages([]Image{imageAMD64, imageSomeArch}) + err = UploadMultiarchImageWithRef(multiImage, baseURL, "test-repo", "index") So(err, ShouldBeNil) // ---------------- BASE IMAGE FOR LINUX AMD64 ------------------- @@ -5105,11 +5089,10 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - multiImage := GetMultiarchImageForImages("latest", []Image{ - imageAMD64, - imageSomeArch, + multiImage := GetMultiarchImageForImages([]Image{ + imageAMD64, imageSomeArch, }) - err = UploadMultiarchImage(multiImage, baseURL, "test-repo") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "test-repo", "latest") So(err, ShouldBeNil) // ---------------- BASE IMAGE ------------------- @@ -5127,11 +5110,11 @@ func RunMetaDBIndexTests(baseURL, port string) { imageAMD64.Layers, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-same-layers", []Image{ + + multiImage = GetMultiarchImageForImages([]Image{ image1, image2, }) - - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-same-layers") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-same-layers", "index-one-arch-same-layers") So(err, ShouldBeNil) // ---------------- SAME LAYERS ------------------- @@ -5150,10 +5133,10 @@ func RunMetaDBIndexTests(baseURL, port string) { [][]byte{imageAMD64.Layers[0]}, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-less-layers", []Image{ + multiImage = GetMultiarchImageForImages([]Image{ image1, image2, }) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-less-layers") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-less-layers", "index-one-arch-less-layers") So(err, ShouldBeNil) // ---------------- LESS LAYERS ------------------- @@ -5172,10 +5155,11 @@ func RunMetaDBIndexTests(baseURL, port string) { [][]byte{{99, 100, 102}}, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-less-layers-false", []Image{ + multiImage = GetMultiarchImageForImages([]Image{ image1, image2, }) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-less-layers-false") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-less-layers-false", + "index-one-arch-less-layers-false") So(err, ShouldBeNil) // ---------------- LESS LAYERS FALSE ------------------- @@ -5198,12 +5182,11 @@ func RunMetaDBIndexTests(baseURL, port string) { }, ) So(err, ShouldBeNil) - multiImage = GetMultiarchImageForImages("index-one-arch-more-layers", []Image{ - image1, - image2, - }) - err = UploadMultiarchImage(multiImage, baseURL, "index-one-arch-more-layers") + multiImage = GetMultiarchImageForImages([]Image{ + image1, image2, + }) + err = UploadMultiarchImageWithRef(multiImage, baseURL, "index-one-arch-more-layers", "index-one-arch-more-layers") So(err, ShouldBeNil) // ---------------- MORE LAYERS ------------------- @@ -5250,8 +5233,7 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - baseLinuxAMD64Digest, err := imageAMD64.Digest() - So(err, ShouldBeNil) + baseLinuxAMD64Digest := imageAMD64.Digest() imageSomeArch, err := GetImageWithComponents( ispec.Image{ @@ -5265,14 +5247,12 @@ func RunMetaDBIndexTests(baseURL, port string) { }) So(err, ShouldBeNil) - baseLinuxSomeArchDigest, err := imageSomeArch.Digest() - So(err, ShouldBeNil) + baseLinuxSomeArchDigest := imageSomeArch.Digest() - multiImage := GetMultiarchImageForImages("index", []Image{ - imageAMD64, - imageSomeArch, + multiImage := GetMultiarchImageForImages([]Image{ + imageAMD64, imageSomeArch, }) - err = UploadMultiarchImage(multiImage, baseURL, "test-repo") + err = UploadMultiarchImageWithRef(multiImage, baseURL, "test-repo", "index") So(err, ShouldBeNil) // ---------------- BASE IMAGE FOR LINUX AMD64 ------------------- @@ -5707,8 +5687,7 @@ func TestMetaDBWhenDeletingImages(t *testing.T) { }) Convey("Delete a referrer", func() { - referredImageDigest, err := image1.Digest() - So(err, ShouldBeNil) + referredImageDigest := image1.Digest() referrerImage, err := GetImageWithSubject(referredImageDigest, ispec.MediaTypeImageManifest) So(err, ShouldBeNil) @@ -6192,8 +6171,7 @@ func TestImageSummary(t *testing.T) { So(err, ShouldBeNil) image.Reference = tagTarget - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() err = UploadImage(image, baseURL, repoName) So(err, ShouldBeNil) @@ -6208,8 +6186,7 @@ func TestImageSummary(t *testing.T) { } referrerImage.Manifest.Config.MediaType = "application/test.artifact.type" referrerImage.Manifest.Annotations = map[string]string{"testAnnotationKey": "testAnnotationValue"} - referrerManifestDigest, err := referrerImage.Digest() - So(err, ShouldBeNil) + referrerManifestDigest := referrerImage.Digest() referrerImage.Reference = referrerManifestDigest.String() err = UploadImage(referrerImage, baseURL, repoName) @@ -6515,8 +6492,7 @@ func TestImageSummary(t *testing.T) { So(err, ShouldBeNil) img1.Manifest.Config = ispec.DescriptorEmptyJSON img1.Manifest.ArtifactType = artType1 - digest1, err := img1.Digest() - So(err, ShouldBeNil) + digest1 := img1.Digest() err = UploadImage(img1, baseURL, "repo") So(err, ShouldBeNil) @@ -6524,8 +6500,7 @@ func TestImageSummary(t *testing.T) { img2, err := GetRandomImage("art2") So(err, ShouldBeNil) img2.Manifest.Config.MediaType = artType2 - digest2, err := img2.Digest() - So(err, ShouldBeNil) + digest2 := img2.Digest() err = UploadImage(img2, baseURL, "repo") So(err, ShouldBeNil) diff --git a/pkg/extensions/sync/sync_test.go b/pkg/extensions/sync/sync_test.go index e9f1e6c0..085f6346 100644 --- a/pkg/extensions/sync/sync_test.go +++ b/pkg/extensions/sync/sync_test.go @@ -4439,13 +4439,10 @@ func TestSyncedSignaturesMetaDB(t *testing.T) { defer scm.StopServer() // Push an image - destImage, err := test.GetRandomImage(tag) + signedImage, err := test.GetRandomImage(tag) So(err, ShouldBeNil) - signedImageDigest, err := destImage.Digest() - So(err, ShouldBeNil) - - err = test.UploadImage(destImage, srcBaseURL, repoName) + err = test.UploadImage(signedImage, srcBaseURL, repoName) So(err, ShouldBeNil) err = test.SignImageUsingNotary(repoName+":"+tag, srcPort) @@ -4497,9 +4494,9 @@ func TestSyncedSignaturesMetaDB(t *testing.T) { So(err, ShouldBeNil) So(repoMeta.Tags, ShouldContainKey, tag) So(len(repoMeta.Tags), ShouldEqual, 1) - So(repoMeta.Signatures, ShouldContainKey, signedImageDigest.String()) + So(repoMeta.Signatures, ShouldContainKey, signedImage.DigestStr()) - imageSignatures := repoMeta.Signatures[signedImageDigest.String()] + imageSignatures := repoMeta.Signatures[signedImage.DigestStr()] So(imageSignatures, ShouldContainKey, signatures.CosignSignature) So(len(imageSignatures[signatures.CosignSignature]), ShouldEqual, 1) So(imageSignatures, ShouldContainKey, signatures.NotationSignature) diff --git a/pkg/meta/meta_test.go b/pkg/meta/meta_test.go index 19dbcdff..c06f19fb 100644 --- a/pkg/meta/meta_test.go +++ b/pkg/meta/meta_test.go @@ -2688,11 +2688,9 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func multiArch, err := test.GetRandomMultiarchImage("tag1") So(err, ShouldBeNil) - indexDigest, err := multiArch.Digest() - So(err, ShouldBeNil) + indexDigest := multiArch.Digest() - indexData, err := multiArch.IndexData() - So(err, ShouldBeNil) + indexData := multiArch.IndexData() err = metaDB.SetIndexData(indexDigest, indexData) So(err, ShouldBeNil) @@ -2709,8 +2707,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func image, err := test.GetRandomImage("tag") So(err, ShouldBeNil) - referredDigest, err := image.Digest() - So(err, ShouldBeNil) + referredDigest := image.Digest() manifestBlob, err := json.Marshal(image.Manifest) So(err, ShouldBeNil) @@ -2737,8 +2734,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func ) So(err, ShouldBeNil) - artifactDigest1, err := artifact1.Digest() - So(err, ShouldBeNil) + artifactDigest1 := artifact1.Digest() err = metaDB.SetReferrer("repo", referredDigest, mTypes.ReferrerInfo{ Digest: artifactDigest1.String(), @@ -2754,8 +2750,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func ) So(err, ShouldBeNil) - artifactDigest2, err := artifact2.Digest() - So(err, ShouldBeNil) + artifactDigest2 := artifact2.Digest() err = metaDB.SetReferrer("repo", referredDigest, mTypes.ReferrerInfo{ Digest: artifactDigest2.String(), @@ -2874,8 +2869,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func Convey("FilterRepos", func() { img, err := test.GetRandomImage("img1") So(err, ShouldBeNil) - imgDigest, err := img.Digest() - So(err, ShouldBeNil) + imgDigest := img.Digest() manifestData, err := NewManifestData(img.Manifest, img.Config) So(err, ShouldBeNil) @@ -2885,8 +2879,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func multiarch, err := test.GetRandomMultiarchImage("multi") So(err, ShouldBeNil) - multiarchDigest, err := multiarch.Digest() - So(err, ShouldBeNil) + multiarchDigest := multiarch.Digest() indexData, err := NewIndexData(multiarch.Index) So(err, ShouldBeNil) @@ -2895,8 +2888,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func So(err, ShouldBeNil) for _, img := range multiarch.Images { - digest, err := img.Digest() - So(err, ShouldBeNil) + digest := img.Digest() indManData1, err := NewManifestData(multiarch.Images[0].Manifest, multiarch.Images[0].Config) So(err, ShouldBeNil) diff --git a/pkg/meta/signatures/signatures_test.go b/pkg/meta/signatures/signatures_test.go index 58760845..bc24976c 100644 --- a/pkg/meta/signatures/signatures_test.go +++ b/pkg/meta/signatures/signatures_test.go @@ -147,8 +147,7 @@ func TestVerifySignatures(t *testing.T) { manifestContent, err := json.Marshal(image.Manifest) So(err, ShouldBeNil) - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() _, _, _, err = signatures.VerifySignature("wrongType", []byte(""), "", manifestDigest, manifestContent, "repo") So(err, ShouldNotBeNil) @@ -164,8 +163,7 @@ func TestVerifySignatures(t *testing.T) { manifestContent, err := json.Marshal(image.Manifest) So(err, ShouldBeNil) - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() Convey("cosignDir is not set", func() { _, _, _, err = signatures.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo) @@ -300,8 +298,7 @@ func TestVerifySignatures(t *testing.T) { manifestContent, err := json.Marshal(image.Manifest) So(err, ShouldBeNil) - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() Convey("notationDir is not set", func() { _, _, _, err = signatures.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo) diff --git a/pkg/meta/storage_parsing_test.go b/pkg/meta/storage_parsing_test.go index 34d76f88..faed8088 100644 --- a/pkg/meta/storage_parsing_test.go +++ b/pkg/meta/storage_parsing_test.go @@ -529,8 +529,7 @@ func RunParseStorageTests(rootDir string, metaDB mTypes.MetaDB) { signatureTag, err := test.GetCosignSignatureTagForManifest(image.Manifest) So(err, ShouldBeNil) - missingImageDigest, err := image.Digest() - So(err, ShouldBeNil) + missingImageDigest := image.Digest() // get the body of the signature config, layers, manifest, err = test.GetRandomImageComponents(100) @@ -576,8 +575,7 @@ func RunParseStorageTests(rootDir string, metaDB mTypes.MetaDB) { image, err := test.GetRandomImage("tag") So(err, ShouldBeNil) - manifestDigest, err := image.Digest() - So(err, ShouldBeNil) + manifestDigest := image.Digest() err = test.WriteImageToFileSystem( image, diff --git a/pkg/test/common.go b/pkg/test/common.go index e7a03630..f3457a2f 100644 --- a/pkg/test/common.go +++ b/pkg/test/common.go @@ -15,13 +15,13 @@ import ( "log" "math" "math/big" + mathRand "math/rand" "net" "net/http" "net/url" "os" "path" "path/filepath" - "runtime" "strings" "sync" "time" @@ -48,8 +48,6 @@ import ( "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" - zerr "zotregistry.io/zot/errors" - mTypes "zotregistry.io/zot/pkg/meta/types" "zotregistry.io/zot/pkg/storage" storageCommon "zotregistry.io/zot/pkg/storage/common" "zotregistry.io/zot/pkg/test/inject" @@ -61,6 +59,8 @@ const ( SleepTime = 100 * time.Millisecond ) +var ErrNoGoModFileFound = errors.New("test: no go.mod file found in parent directories") + var vulnerableLayer []byte //nolint: gochecknoglobals var NotationPathLock = new(sync.Mutex) //nolint: gochecknoglobals @@ -97,51 +97,11 @@ var ( ErrPutIndex = errors.New("can't put index") ) -type Image struct { - Manifest ispec.Manifest - Config ispec.Image - Layers [][]byte - Reference string -} - -func (img Image) Digest() (godigest.Digest, error) { - blob, err := json.Marshal(img.Manifest) - if err != nil { - return "", err - } - - return godigest.FromBytes(blob), nil -} - type ArtifactBlobs struct { Blob []byte MediaType string } -type MultiarchImage struct { - Index ispec.Index - Images []Image - Reference string -} - -func (mi *MultiarchImage) Digest() (godigest.Digest, error) { - indexBlob, err := json.Marshal(mi.Index) - if err != nil { - return "", err - } - - return godigest.FromBytes(indexBlob), nil -} - -func (mi *MultiarchImage) IndexData() (mTypes.IndexData, error) { - indexBlob, err := json.Marshal(mi.Index) - if err != nil { - return mTypes.IndexData{}, err - } - - return mTypes.IndexData{IndexBlob: indexBlob}, nil -} - func GetFreePort() string { port, err := freeport.GetFreePort() if err != nil { @@ -362,7 +322,7 @@ func WriteImageToFileSystem(image Image, repoName string, storeController storag return nil } -func WriteMultiArchImageToFileSystem(multiarchImage MultiarchImage, repoName string, +func WriteMultiArchImageToFileSystem(multiarchImage MultiarchImage, repoName, ref string, storeController storage.StoreController, ) error { store := storeController.GetImageStore(repoName) @@ -384,7 +344,7 @@ func WriteMultiArchImageToFileSystem(multiarchImage MultiarchImage, repoName str return err } - _, _, err = store.PutImageManifest(repoName, multiarchImage.Reference, ispec.MediaTypeImageIndex, + _, _, err = store.PutImageManifest(repoName, ref, ispec.MediaTypeImageIndex, indexBlob) return err @@ -646,47 +606,8 @@ const ( Vulnerability3ID = "CVE-2023-2975" ) -func GetVulnImage(ref string) (Image, error) { - const skipStackFrame = 2 - - vulnerableLayer, err := GetLayerWithVulnerability(skipStackFrame) - if err != nil { - return Image{}, err - } - - vulnerableConfig := ispec.Image{ - Platform: ispec.Platform{ - Architecture: "amd64", - OS: "linux", - }, - Config: ispec.ImageConfig{ - Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, - Cmd: []string{"/bin/sh"}, - }, - RootFS: ispec.RootFS{ - Type: "layers", - DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"}, - }, - } - - img, err := GetImageWithComponents( - vulnerableConfig, - [][]byte{ - vulnerableLayer, - }) - if err != nil { - return Image{}, err - } - - img.Reference = ref - - return img, err -} - func GetVulnImageWithConfig(ref string, config ispec.Image) (Image, error) { - const skipStackFrame = 2 - - vulnerableLayer, err := GetLayerWithVulnerability(skipStackFrame) + vulnerableLayer, err := GetLayerWithVulnerability() if err != nil { return Image{}, err } @@ -716,44 +637,22 @@ func GetVulnImageWithConfig(ref string, config ispec.Image) (Image, error) { return img, err } -func GetLayerWithVulnerability(skip int) ([]byte, error) { +func GetLayerWithVulnerability() ([]byte, error) { if vulnerableLayer != nil { return vulnerableLayer, nil } - _, b, _, ok := runtime.Caller(skip) - if !ok { - return []byte{}, zerr.ErrCallerInfo + projectRootDir, err := GetProjectRootDir() + if err != nil { + return nil, err } - absoluteCallerpath := filepath.Dir(b) - fmt.Println(absoluteCallerpath) - - // we know pkg folder inside zot must exist, and since all tests are called from within pkg we'll use it as reference - relCallerPath := absoluteCallerpath[strings.LastIndex(absoluteCallerpath, "pkg"):] - - relCallerSlice := strings.Split(relCallerPath, string(os.PathSeparator)) - fmt.Println(relCallerPath, relCallerSlice) - - // we'll calculate how many folder we should go back to reach the root of the zot folder relative - // to the callers position - backPathSlice := make([]string, len(relCallerSlice)) - - for i := 0; i < len(backPathSlice); i++ { - backPathSlice[i] = ".." - } - - backPath := filepath.Join(backPathSlice...) - // this is the path of the blob relative to the root of the zot folder vulnBlobPath := "test/data/alpine/blobs/sha256/f56be85fc22e46face30e2c3de3f7fe7c15f8fd7c4e5add29d7f64b87abdaa09" - var err error + absoluteVulnBlobPath, _ := filepath.Abs(filepath.Join(projectRootDir, vulnBlobPath)) - x, _ := filepath.Abs(filepath.Join(backPath, vulnBlobPath)) - _ = x - - vulnerableLayer, err = os.ReadFile(filepath.Join(backPath, vulnBlobPath)) //nolint: lll + vulnerableLayer, err := os.ReadFile(absoluteVulnBlobPath) //nolint: lll if err != nil { return nil, err } @@ -761,6 +660,28 @@ func GetLayerWithVulnerability(skip int) ([]byte, error) { return vulnerableLayer, nil } +func GetProjectRootDir() (string, error) { + workDir, err := os.Getwd() + if err != nil { + return "", err + } + + for { + goModPath := filepath.Join(workDir, "go.mod") + + _, err := os.Stat(goModPath) + if err == nil { + return workDir, nil + } + + if workDir == filepath.Dir(workDir) { + return "", ErrNoGoModFileFound + } + + workDir = filepath.Dir(workDir) + } +} + func GetRandomLayer(size int) []byte { layer := make([]byte, size) @@ -975,10 +896,17 @@ func UploadImage(img Image, baseURL, repo string) error { return ErrPutBlob } } - // upload config - cblob, err := json.Marshal(img.Config) - if err = inject.Error(err); err != nil { - return err + + var err error + + cblob := img.ConfigDescriptor.Data + + // we'll remove this check once we make the full transition to the new way of generating test images + if len(cblob) == 0 { + cblob, err = json.Marshal(img.Config) + if err = inject.Error(err); err != nil { + return err + } } cdigest := godigest.FromBytes(cblob) @@ -1016,10 +944,14 @@ func UploadImage(img Image, baseURL, repo string) error { return ErrPostBlob } - // put manifest - manifestBlob, err := json.Marshal(img.Manifest) - if err = inject.Error(err); err != nil { - return err + manifestBlob := img.ManifestDescriptor.Data + + // we'll remove this check once we make the full transition to the new way of generating test images + if len(manifestBlob) == 0 { + manifestBlob, err = json.Marshal(img.Manifest) + if err = inject.Error(err); err != nil { + return err + } } // validate manifest @@ -1047,6 +979,115 @@ func UploadImage(img Image, baseURL, repo string) error { return err } +func UploadImageWithRef(img Image, baseURL, repo, ref string) error { + for _, blob := range img.Layers { + resp, err := resty.R().Post(baseURL + "/v2/" + repo + "/blobs/uploads/") + if err != nil { + return err + } + + if resp.StatusCode() != http.StatusAccepted { + return ErrPostBlob + } + + loc := resp.Header().Get("Location") + + digest := godigest.FromBytes(blob).String() + + resp, err = resty.R(). + SetHeader("Content-Length", fmt.Sprintf("%d", len(blob))). + SetHeader("Content-Type", "application/octet-stream"). + SetQueryParam("digest", digest). + SetBody(blob). + Put(baseURL + loc) + + if err != nil { + return err + } + + if resp.StatusCode() != http.StatusCreated { + return ErrPutBlob + } + } + + var err error + + cblob := img.ConfigDescriptor.Data + + // we'll remove this check once we make the full transition to the new way of generating test images + if len(cblob) == 0 { + cblob, err = json.Marshal(img.Config) + if err = inject.Error(err); err != nil { + return err + } + } + + cdigest := godigest.FromBytes(cblob) + + if img.Manifest.Config.MediaType == ispec.MediaTypeEmptyJSON || + img.Manifest.Config.Digest == ispec.DescriptorEmptyJSON.Digest { + cblob = ispec.DescriptorEmptyJSON.Data + cdigest = ispec.DescriptorEmptyJSON.Digest + } + + resp, err := resty.R(). + Post(baseURL + "/v2/" + repo + "/blobs/uploads/") + if err = inject.Error(err); err != nil { + return err + } + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusAccepted || inject.ErrStatusCode(resp.StatusCode()) == -1 { + return ErrPostBlob + } + + loc := Location(baseURL, resp) + + // uploading blob should get 201 + resp, err = resty.R(). + SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))). + SetHeader("Content-Type", "application/octet-stream"). + SetQueryParam("digest", cdigest.String()). + SetBody(cblob). + Put(loc) + if err = inject.Error(err); err != nil { + return err + } + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusCreated || inject.ErrStatusCode(resp.StatusCode()) == -1 { + return ErrPostBlob + } + + manifestBlob := img.ManifestDescriptor.Data + + // we'll remove this check once we make the full transition to the new way of generating test images + if len(manifestBlob) == 0 { + manifestBlob, err = json.Marshal(img.Manifest) + if err = inject.Error(err); err != nil { + return err + } + } + + // validate manifest + if err := storageCommon.ValidateManifestSchema(manifestBlob); err != nil { + return err + } + + resp, err = resty.R(). + SetHeader("Content-type", ispec.MediaTypeImageManifest). + SetBody(manifestBlob). + Put(baseURL + "/v2/" + repo + "/manifests/" + ref) + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusCreated { + return ErrPutBlob + } + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusCreated { + return ErrPutBlob + } + + return err +} + func DeleteImage(repo, reference, baseURL string) (int, error) { resp, err := resty.R().Delete( fmt.Sprintf(baseURL+"/v2/%s/manifests/%s", repo, reference), @@ -1715,6 +1756,96 @@ func UploadImageWithBasicAuth(img Image, baseURL, repo, user, password string) e return err } +func UploadImageWithBasicAuthRef(img Image, baseURL, repo, ref, user, password string) error { + for _, blob := range img.Layers { + resp, err := resty.R(). + SetBasicAuth(user, password). + Post(baseURL + "/v2/" + repo + "/blobs/uploads/") + if err != nil { + return err + } + + if resp.StatusCode() != http.StatusAccepted { + return ErrPostBlob + } + + loc := resp.Header().Get("Location") + + digest := godigest.FromBytes(blob).String() + + resp, err = resty.R(). + SetBasicAuth(user, password). + SetHeader("Content-Length", fmt.Sprintf("%d", len(blob))). + SetHeader("Content-Type", "application/octet-stream"). + SetQueryParam("digest", digest). + SetBody(blob). + Put(baseURL + loc) + + if err != nil { + return err + } + + if resp.StatusCode() != http.StatusCreated { + return ErrPutBlob + } + } + // upload config + cblob, err := json.Marshal(img.Config) + if err = inject.Error(err); err != nil { + return err + } + + cdigest := godigest.FromBytes(cblob) + + if img.Manifest.Config.MediaType == ispec.MediaTypeEmptyJSON { + cblob = ispec.DescriptorEmptyJSON.Data + cdigest = ispec.DescriptorEmptyJSON.Digest + } + + resp, err := resty.R(). + SetBasicAuth(user, password). + Post(baseURL + "/v2/" + repo + "/blobs/uploads/") + if err = inject.Error(err); err != nil { + return err + } + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusAccepted || inject.ErrStatusCode(resp.StatusCode()) == -1 { + return ErrPostBlob + } + + loc := Location(baseURL, resp) + + // uploading blob should get 201 + resp, err = resty.R(). + SetBasicAuth(user, password). + SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))). + SetHeader("Content-Type", "application/octet-stream"). + SetQueryParam("digest", cdigest.String()). + SetBody(cblob). + Put(loc) + if err = inject.Error(err); err != nil { + return err + } + + if inject.ErrStatusCode(resp.StatusCode()) != http.StatusCreated || inject.ErrStatusCode(resp.StatusCode()) == -1 { + return ErrPostBlob + } + + // put manifest + manifestBlob, err := json.Marshal(img.Manifest) + if err = inject.Error(err); err != nil { + return err + } + + _, err = resty.R(). + SetBasicAuth(user, password). + SetHeader("Content-type", "application/vnd.oci.image.manifest.v1+json"). + SetBody(manifestBlob). + Put(baseURL + "/v2/" + repo + "/manifests/" + ref) + + return err +} + func SignImageUsingCosign(repoTag, port string) error { cwd, err := os.Getwd() if err != nil { @@ -1900,7 +2031,7 @@ func GetRandomMultiarchImage(reference string) (MultiarchImage, error) { }, err } -func GetMultiarchImageForImages(reference string, images []Image) MultiarchImage { +func GetMultiarchImageForImages(images []Image) MultiarchImage { var index ispec.Index for i, image := range images { @@ -1916,7 +2047,7 @@ func GetMultiarchImageForImages(reference string, images []Image) MultiarchImage index.SchemaVersion = 2 - return MultiarchImage{Index: index, Images: images, Reference: reference} + return MultiarchImage{Index: index, Images: images} } func getManifestSize(manifest ispec.Manifest) int64 { @@ -1968,6 +2099,37 @@ func UploadMultiarchImage(multiImage MultiarchImage, baseURL string, repo string return err } +func UploadMultiarchImageWithRef(multiImage MultiarchImage, baseURL string, repo, ref string) error { + for _, image := range multiImage.Images { + err := UploadImageWithRef(image, baseURL, repo, image.DigestStr()) + if err != nil { + return err + } + } + + // put manifest + indexBlob, err := json.Marshal(multiImage.Index) + if err = inject.Error(err); err != nil { + return err + } + + // validate manifest + if err := storageCommon.ValidateImageIndexSchema(indexBlob); err != nil { + return err + } + + resp, err := resty.R(). + SetHeader("Content-type", ispec.MediaTypeImageIndex). + SetBody(indexBlob). + Put(baseURL + "/v2/" + repo + "/manifests/" + ref) + + if resp.StatusCode() != http.StatusCreated { + return ErrPutIndex + } + + return err +} + func GetIndexBlobWithManifests(manifestDigests []godigest.Digest) ([]byte, error) { manifests := make([]ispec.Descriptor, 0, len(manifestDigests)) @@ -2037,3 +2199,87 @@ func CustomRedirectPolicy(noOfRedirect int) resty.RedirectPolicy { return nil }) } + +func DateRef(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) *time.Time { + date := time.Date(year, month, day, hour, min, sec, nsec, loc) + + return &date +} + +func RandomDateRef(loc *time.Location) *time.Time { + var ( + year = 1990 + mathRand.Intn(30) //nolint: gosec,gomnd + month = time.Month(1 + mathRand.Intn(10)) //nolint: gosec,gomnd + day = 1 + mathRand.Intn(5) //nolint: gosec,gomnd + hour = 1 + mathRand.Intn(22) //nolint: gosec,gomnd + min = 1 + mathRand.Intn(58) //nolint: gosec,gomnd + sec = 1 + mathRand.Intn(58) //nolint: gosec,gomnd + nsec = 1 + ) + + return DateRef(year, month, day, hour, min, sec, nsec, time.UTC) +} + +func GetDefaultConfig() ispec.Image { + return ispec.Image{ + Created: DefaultTimeRef(), + Author: "ZotUser", + Platform: ispec.Platform{ + OS: "linux", + Architecture: "amd64", + }, + RootFS: ispec.RootFS{ + Type: "layers", + DiffIDs: []godigest.Digest{}, + }, + } +} + +func GetDefaultVulnConfig() ispec.Image { + return ispec.Image{ + Created: DefaultTimeRef(), + Author: "ZotUser", + Platform: ispec.Platform{ + Architecture: "amd64", + OS: "linux", + }, + Config: ispec.ImageConfig{ + Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + Cmd: []string{"/bin/sh"}, + }, + RootFS: ispec.RootFS{ + Type: "layers", + DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"}, + }, + } +} + +func DefaultTimeRef() *time.Time { + var ( + year = 2010 + month = time.Month(1) + day = 1 + hour = 1 + min = 1 + sec = 1 + nsec = 0 + ) + + return DateRef(year, month, day, hour, min, sec, nsec, time.UTC) +} + +func GetDefaultLayers() []Layer { + return []Layer{ + {Blob: []byte("abc"), Digest: godigest.FromBytes([]byte("abc")), MediaType: ispec.MediaTypeImageLayerGzip}, + {Blob: []byte("123"), Digest: godigest.FromBytes([]byte("123")), MediaType: ispec.MediaTypeImageLayerGzip}, + {Blob: []byte("xyz"), Digest: godigest.FromBytes([]byte("xyz")), MediaType: ispec.MediaTypeImageLayerGzip}, + } +} + +func GetDefaultLayersBlobs() [][]byte { + return [][]byte{ + []byte("abc"), + []byte("123"), + []byte("xyz"), + } +} diff --git a/pkg/test/common_test.go b/pkg/test/common_test.go index ca90c6ca..c1e75e76 100644 --- a/pkg/test/common_test.go +++ b/pkg/test/common_test.go @@ -661,17 +661,29 @@ func TestUploadImage(t *testing.T) { Convey("Request fail while pushing layer", func() { err := test.UploadImageWithBasicAuth(test.Image{Layers: [][]byte{{1, 2, 3}}}, "badURL", "", "", "") So(err, ShouldNotBeNil) + + err = test.UploadImageWithBasicAuthRef(test.Image{Layers: [][]byte{{1, 2, 3}}}, "badURL", "", "", "", "") + So(err, ShouldNotBeNil) }) Convey("Request status is not StatusOk while pushing layer", func() { - err := test.UploadImageWithBasicAuth(test.Image{Layers: [][]byte{{1, 2, 3}}}, baseURL, "repo", "", "") + err := test.UploadImageWithBasicAuth(test.Image{Layers: [][]byte{{1, 2, 3}}}, baseURL, "", "repo", "") + So(err, ShouldNotBeNil) + + err = test.UploadImageWithBasicAuthRef(test.Image{Layers: [][]byte{{1, 2, 3}}}, baseURL, "", "repo", "", "") So(err, ShouldNotBeNil) }) Convey("Request fail while pushing config", func() { err := test.UploadImageWithBasicAuth(test.Image{}, "badURL", "", "", "") So(err, ShouldNotBeNil) + + err = test.UploadImageWithBasicAuthRef(test.Image{}, "badURL", "", "", "", "") + So(err, ShouldNotBeNil) }) Convey("Request status is not StatusOk while pushing config", func() { - err := test.UploadImageWithBasicAuth(test.Image{}, baseURL, "repo", "", "") + err := test.UploadImageWithBasicAuth(test.Image{}, baseURL, "", "repo", "") + So(err, ShouldNotBeNil) + + err = test.UploadImageWithBasicAuthRef(test.Image{}, baseURL, "", "repo", "", "") So(err, ShouldNotBeNil) }) }) @@ -819,6 +831,9 @@ func TestInjectUploadImage(t *testing.T) { if injected { err := test.UploadImage(img, baseURL, "test") So(err, ShouldNotBeNil) + + err = test.UploadImageWithRef(img, baseURL, "test", img.DigestStr()) + So(err, ShouldNotBeNil) } }) Convey("CreateBlobUpload POST call", func() { @@ -826,6 +841,9 @@ func TestInjectUploadImage(t *testing.T) { if injected { err := test.UploadImage(img, baseURL, "test") So(err, ShouldNotBeNil) + + err = test.UploadImageWithRef(img, baseURL, "test", img.DigestStr()) + So(err, ShouldNotBeNil) } }) Convey("UpdateBlobUpload PUT call", func() { @@ -833,6 +851,9 @@ func TestInjectUploadImage(t *testing.T) { if injected { err := test.UploadImage(img, baseURL, "test") So(err, ShouldNotBeNil) + + err = test.UploadImageWithRef(img, baseURL, "test", img.DigestStr()) + So(err, ShouldNotBeNil) } }) Convey("second marshal", func() { @@ -840,6 +861,9 @@ func TestInjectUploadImage(t *testing.T) { if injected { err := test.UploadImage(img, baseURL, "test") So(err, ShouldNotBeNil) + + err = test.UploadImageWithRef(img, baseURL, "test", img.DigestStr()) + So(err, ShouldNotBeNil) } }) }) diff --git a/pkg/test/images.go b/pkg/test/images.go new file mode 100644 index 00000000..61af54b2 --- /dev/null +++ b/pkg/test/images.go @@ -0,0 +1,478 @@ +package test + +import ( + "crypto/rand" + "encoding/json" + mathRand "math/rand" + "strconv" + "time" + + godigest "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + ispec "github.com/opencontainers/image-spec/specs-go/v1" + + storageConstants "zotregistry.io/zot/pkg/storage/constants" +) + +// LayerBuilder abstracts the first step in creating an OCI image, specifying the layers of the image. +type LayerBuilder interface { + // LayerBlobs sets the image layers from the gives blobs array, adding a default zipped layer media type. + LayerBlobs(layers [][]byte) ConfigBuilder + // Layers sets the given layers to the built image + Layers(layers []Layer) ConfigBuilder + // RandomLayers generates `count` layers with the given size and initialises them with random values + // and a default zipped layer media type. + RandomLayers(count, size int) ConfigBuilder + // EmptyLayer adds a single empty json layer semnifying no layers. + EmptyLayer() ConfigBuilder + // DefaultLayers adds predefined default layers. + DefaultLayers() ConfigBuilder + // VulnerableLayers adds layers that contains known CVE's. + VulnerableLayers() VulnerableConfigBuilder +} + +// ConfigBuilder abstracts the second step in creating an OCI image, specifying the config content of the image. +type ConfigBuilder interface { + // ImageConfig sets the given image config. It updates the "config" field of the image manifest with + // values corresponding to the given image. + ImageConfig(config ispec.Image) ManifestBuilder + // ImageConfig sets an empty json as the images config. It updates the "config" field of the image manifest with + // values corresponding to the empty descriptor. + EmptyConfig() ManifestBuilder + // ArtifactConfig sets an empty json as the content of the image config and sets it's media type (described by + // the Config field of the image manifest) to the given artifact type. This will make the created image + // an OCI artifact. + // (see: https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage) + ArtifactConfig(artifactType string) ManifestBuilder + // DefaultConfig sets the default config, platform linux/amd64. + DefaultConfig() ManifestBuilder + // RandomConfig sets a randomly generated config. + RandomConfig() ManifestBuilder +} + +// VulnerableConfigBuilder abstracts specifying the config of an vulnerage OCI image. +// Keeping the RootFS field consistent with the vulnerable layers. +type VulnerableConfigBuilder interface { + // VulnerableConfig sets the given config while keeping the correct RootFS values for the + // vulnerable layer set earlier. This allows scan tools to find CVE's + VulnerableConfig(config ispec.Image) ManifestBuilder + // DefaultVulnConfig sets default config of the vulnerable image + DefaultVulnConfig() ManifestBuilder + // RandomVulnConfig sets the keeping the correct RootFS values for the + // vulnerable layer set earlier. This allows scan tools to find CVE's + RandomVulnConfig() ManifestBuilder +} + +// ManifestBuilder abstracts creating the manifest of the image. +type ManifestBuilder interface { + // Subject sets the subject of the image manifest. + Subject(subject *ispec.Descriptor) ManifestBuilder + // ArtifactType sets the artifact type field on the image manifest, + // (see: https://github.com/opencontainers/image-spec/blob/main/manifest.md#guidelines-for-artifact-usage) + ArtifactType(artifactType string) ManifestBuilder + // Annotations sets the annotations field on the image manifest. + Annotations(annotations map[string]string) ManifestBuilder + + Build() Image +} + +type Image struct { + Manifest ispec.Manifest + Config ispec.Image + Layers [][]byte + Reference string + + ConfigDescriptor ispec.Descriptor + ManifestDescriptor ispec.Descriptor +} + +func (img *Image) Digest() godigest.Digest { + blob, err := json.Marshal(img.Manifest) + if err != nil { + panic("unreachable: ispec.Manifest should always be marshable") + } + + // when we'll migrate all code to the new format of creating images we can replace this with + // the value from manifestDescriptor + return godigest.FromBytes(blob) +} + +func (img *Image) DigestStr() string { + return img.Digest().String() +} + +func (img Image) Descriptor() ispec.Descriptor { + return ispec.Descriptor{ + MediaType: img.ManifestDescriptor.MediaType, + Digest: img.ManifestDescriptor.Digest, + Size: img.ManifestDescriptor.Size, + } +} + +func (img Image) DescriptorRef() *ispec.Descriptor { + return &ispec.Descriptor{ + MediaType: img.ManifestDescriptor.MediaType, + Digest: img.ManifestDescriptor.Digest, + Size: img.ManifestDescriptor.Size, + } +} + +type Layer struct { + Blob []byte + MediaType string + Digest godigest.Digest +} + +// CreateImageWith initiates the creation of an OCI image. The creation process starts with +// specifying the layers of the image. +func CreateImageWith() LayerBuilder { + // set default values here + return &BaseImageBuilder{} +} + +func CreateDefaultImage() Image { + return CreateImageWith().DefaultLayers().DefaultConfig().Build() +} + +func CreateDefaultImageWith() ManifestBuilder { + return CreateImageWith().DefaultLayers().DefaultConfig() +} + +const ( + layerCount = 1 + layerSize = 10 +) + +func CreateRandomImage() Image { + return CreateImageWith().RandomLayers(layerCount, layerSize).RandomConfig().Build() +} + +func CreateRandomImageWith() ManifestBuilder { + return CreateImageWith().RandomLayers(layerCount, layerSize).RandomConfig() +} + +func CreateVulnerableImage() Image { + return CreateImageWith().VulnerableLayers().DefaultVulnConfig().Build() +} + +func CreateRandomVulnerableImage() Image { + return CreateImageWith().VulnerableLayers().RandomVulnConfig().Build() +} + +func CreateRandomVulnerableImageWith() ManifestBuilder { + return CreateImageWith().VulnerableLayers().RandomVulnConfig() +} + +type BaseImageBuilder struct { + layers []Layer + + config ispec.Image + configDescriptor ispec.Descriptor + + annotations map[string]string + subject *ispec.Descriptor + artifactType string +} + +func (ib *BaseImageBuilder) Layers(layers []Layer) ConfigBuilder { + ib.layers = layers + + return ib +} + +func (ib *BaseImageBuilder) LayerBlobs(layers [][]byte) ConfigBuilder { + for _, layer := range layers { + ib.layers = append(ib.layers, Layer{ + Blob: layer, + MediaType: ispec.MediaTypeImageLayerGzip, + Digest: godigest.FromBytes(layer), + }) + } + + return ib +} + +func (ib *BaseImageBuilder) EmptyLayer() ConfigBuilder { + ib.layers = []Layer{ + { + Blob: ispec.DescriptorEmptyJSON.Data, + MediaType: ispec.DescriptorEmptyJSON.MediaType, + Digest: ispec.DescriptorEmptyJSON.Digest, + }, + } + + return ib +} + +func (ib *BaseImageBuilder) RandomLayers(count, size int) ConfigBuilder { + for i := 0; i < count; i++ { + layer := make([]byte, size) + + _, err := rand.Read(layer) + if err != nil { + panic("unexpected error while reading random bytes") + } + + ib.layers = append(ib.layers, Layer{ + Blob: layer, + MediaType: ispec.MediaTypeImageLayerGzip, + Digest: godigest.FromBytes(layer), + }) + } + + return ib +} + +func (ib *BaseImageBuilder) DefaultLayers() ConfigBuilder { + ib.layers = GetDefaultLayers() + + return ib +} + +func (ib *BaseImageBuilder) VulnerableLayers() VulnerableConfigBuilder { + layer, err := GetLayerWithVulnerability() + if err != nil { + panic("unable to read vulnerable layers from test data: " + err.Error()) + } + + ib.layers = []Layer{ + { + Blob: layer, + MediaType: ispec.MediaTypeImageLayerGzip, + Digest: godigest.FromBytes(layer), + }, + } + + return ib +} + +func (ib *BaseImageBuilder) ImageConfig(config ispec.Image) ManifestBuilder { + ib.config = config + + configBlob, err := json.Marshal(config) + if err != nil { + panic("unreachable: ispec.Image should always be marshable") + } + + ib.configDescriptor = ispec.Descriptor{ + MediaType: ispec.MediaTypeImageConfig, + Size: int64(len(configBlob)), + Data: configBlob, + Digest: godigest.FromBytes(configBlob), + } + + return ib +} + +func (ib *BaseImageBuilder) DefaultConfig() ManifestBuilder { + return ib.ImageConfig(GetDefaultConfig()) +} + +func (ib *BaseImageBuilder) EmptyConfig() ManifestBuilder { + ib.configDescriptor = ispec.DescriptorEmptyJSON + + return ib +} + +func (ib *BaseImageBuilder) ArtifactConfig(artifactType string) ManifestBuilder { + configDescriptor := ispec.DescriptorEmptyJSON + configDescriptor.MediaType = artifactType + + ib.configDescriptor = configDescriptor + + return ib +} + +func (ib *BaseImageBuilder) RandomConfig() ManifestBuilder { + config := GetDefaultConfig() + config.Author = getRandomAuthor() + config.Platform = getRandomPlatform() + config.Created = RandomDateRef(time.UTC) + + ib.config = config + + configBlob, err := json.Marshal(config) + if err != nil { + panic("unreachable: ispec.Image should always be marshable") + } + + ib.configDescriptor = ispec.Descriptor{ + MediaType: ispec.MediaTypeImageConfig, + Digest: godigest.FromBytes(configBlob), + Size: int64(len(configBlob)), + Data: configBlob, + } + + return ib +} + +func (ib *BaseImageBuilder) DefaultVulnConfig() ManifestBuilder { + vulnerableConfig := GetDefaultVulnConfig() + + configBlob, err := json.Marshal(vulnerableConfig) + if err != nil { + panic("unreachable: ispec.Image should always be marshable") + } + + vulnConfigDescriptor := ispec.Descriptor{ + MediaType: ispec.MediaTypeImageConfig, + Digest: godigest.FromBytes(configBlob), + Size: int64(len(configBlob)), + Data: configBlob, + } + + ib.config = vulnerableConfig + ib.configDescriptor = vulnConfigDescriptor + + return ib +} + +func (ib *BaseImageBuilder) VulnerableConfig(config ispec.Image) ManifestBuilder { + vulnerableConfig := ispec.Image{ + Created: config.Created, + Platform: config.Platform, + Config: config.Config, + RootFS: ispec.RootFS{ + Type: "layers", + DiffIDs: []godigest.Digest{"sha256:f1417ff83b319fbdae6dd9cd6d8c9c88002dcd75ecf6ec201c8c6894681cf2b5"}, + }, + Author: config.Author, + History: config.History, + } + + configBlob, err := json.Marshal(vulnerableConfig) + if err != nil { + panic("unreachable: ispec.Image should always be marshable") + } + + vulnConfigDescriptor := ispec.Descriptor{ + MediaType: ispec.MediaTypeImageConfig, + Digest: godigest.FromBytes(configBlob), + Size: int64(len(configBlob)), + Data: configBlob, + } + + ib.config = vulnerableConfig + ib.configDescriptor = vulnConfigDescriptor + + return ib +} + +func (ib *BaseImageBuilder) RandomVulnConfig() ManifestBuilder { + vulnerableConfig := GetDefaultVulnConfig() + + vulnerableConfig.Author = getRandomAuthor() + vulnerableConfig.Platform = getRandomPlatform() + vulnerableConfig.Created = RandomDateRef(time.UTC) + + configBlob, err := json.Marshal(vulnerableConfig) + if err != nil { + panic("unreachable: ispec.Image should always be marshable") + } + + vulnConfigDescriptor := ispec.Descriptor{ + MediaType: ispec.MediaTypeImageConfig, + Digest: godigest.FromBytes(configBlob), + Size: int64(len(configBlob)), + Data: configBlob, + } + + ib.config = vulnerableConfig + ib.configDescriptor = vulnConfigDescriptor + + return ib +} + +func (ib *BaseImageBuilder) Subject(subject *ispec.Descriptor) ManifestBuilder { + ib.subject = subject + + return ib +} + +func (ib *BaseImageBuilder) ArtifactType(artifactType string) ManifestBuilder { + ib.artifactType = artifactType + + return ib +} + +func (ib *BaseImageBuilder) Annotations(annotations map[string]string) ManifestBuilder { + ib.annotations = annotations + + return ib +} + +func (ib *BaseImageBuilder) Build() Image { + img := Image{ + Layers: getLayerBlobs(ib.layers), + Config: ib.config, + ConfigDescriptor: ib.configDescriptor, + Manifest: ispec.Manifest{ + Versioned: specs.Versioned{SchemaVersion: storageConstants.SchemaVersion}, + MediaType: ispec.MediaTypeImageManifest, + Config: ispec.Descriptor{ + MediaType: ib.configDescriptor.MediaType, + Digest: ib.configDescriptor.Digest, + Size: ib.configDescriptor.Size, + }, + Layers: getLayersDescriptors(ib.layers), + ArtifactType: ib.artifactType, + Subject: ib.subject, + Annotations: ib.annotations, + }, + } + + manifestBlob, err := json.Marshal(img.Manifest) + if err != nil { + panic("unreachable: ispec.Manifest should always be marshable") + } + + img.ManifestDescriptor = ispec.Descriptor{ + MediaType: ispec.MediaTypeImageManifest, + Digest: godigest.FromBytes(manifestBlob), + Size: int64(len(manifestBlob)), + Data: manifestBlob, + } + + img.Reference = img.ManifestDescriptor.Digest.String() + + return img +} + +func getRandomAuthor() string { + const n = 100000 + + return "ZotUser-" + strconv.Itoa(mathRand.Intn(n)) //nolint: gosec +} + +func getRandomPlatform() ispec.Platform { + const n = 100000 + + return ispec.Platform{ + OS: "linux-" + strconv.Itoa(mathRand.Intn(n)), //nolint: gosec + Architecture: "amd64-" + strconv.Itoa(mathRand.Intn(n)), //nolint: gosec + } +} + +func getLayerBlobs(layers []Layer) [][]byte { + blobs := make([][]byte, len(layers)) + + for i := range layers { + blobs[i] = layers[i].Blob + } + + return blobs +} + +func getLayersDescriptors(layers []Layer) []ispec.Descriptor { + descriptors := make([]ispec.Descriptor, len(layers)) + + for i := range layers { + descriptors[i] = ispec.Descriptor{ + Digest: layers[i].Digest, + MediaType: layers[i].MediaType, + Size: int64(len(layers[i].Blob)), + } + } + + return descriptors +} diff --git a/pkg/test/images_test.go b/pkg/test/images_test.go new file mode 100644 index 00000000..c14aed44 --- /dev/null +++ b/pkg/test/images_test.go @@ -0,0 +1,200 @@ +package test_test + +import ( + "encoding/json" + "testing" + + godigest "github.com/opencontainers/go-digest" + ispec "github.com/opencontainers/image-spec/specs-go/v1" + . "github.com/smartystreets/goconvey/convey" + + "zotregistry.io/zot/pkg/test" +) + +func TestImageBuilder(t *testing.T) { + vulnLayer, err := test.GetLayerWithVulnerability() + if err != nil { + t.FailNow() + } + + Convey("Test Layer Builders", t, func() { + layerBuilder := test.CreateImageWith() + + Convey("LayerBlobs", func() { + layerBlobs := [][]byte{{11, 11, 11}, {22, 22, 22}} + + image := layerBuilder. + LayerBlobs(layerBlobs). + DefaultConfig(). + Build() + + So(image.Layers, ShouldResemble, layerBlobs) + So(image.Config, ShouldResemble, test.GetDefaultConfig()) + }) + + Convey("DefaultLayers", func() { + image := layerBuilder. + DefaultLayers(). + DefaultConfig(). + Build() + + So(image.Layers, ShouldResemble, test.GetDefaultLayersBlobs()) + So(image.Config, ShouldResemble, test.GetDefaultConfig()) + }) + + Convey("Layers", func() { + blob1, blob2 := []byte{10, 10, 10}, []byte{20, 20, 20} + + layers := []test.Layer{ + { + Blob: blob1, + MediaType: ispec.MediaTypeImageLayerGzip, + Digest: godigest.FromBytes(blob1), + }, + { + Blob: blob2, + MediaType: ispec.MediaTypeImageLayerGzip, + Digest: godigest.FromBytes(blob2), + }, + } + image := layerBuilder. + Layers(layers). + DefaultConfig(). + Build() + + So(image.Layers, ShouldResemble, [][]byte{blob1, blob2}) + So(image.Config, ShouldResemble, test.GetDefaultConfig()) + }) + + Convey("Empty Layer", func() { + image := layerBuilder. + EmptyLayer(). + DefaultConfig(). + Build() + + So(image.Layers, ShouldResemble, [][]byte{ispec.DescriptorEmptyJSON.Data}) + }) + }) + + Convey("Config builder", t, func() { + configBuilder := test.CreateImageWith().DefaultLayers() + + Convey("Empty Config", func() { + img := configBuilder.EmptyConfig().Build() + So(img.Manifest.Config.Size, ShouldEqual, ispec.DescriptorEmptyJSON.Size) + So(img.Manifest.Config.Digest, ShouldResemble, ispec.DescriptorEmptyJSON.Digest) + So(img.Reference, ShouldResemble, img.Digest().String()) + }) + }) + + Convey("Vulnerable config builder", t, func() { + configBuilder := test.CreateImageWith().VulnerableLayers() + + Convey("VulnerableConfig", func() { + platform := ispec.Platform{OS: "os", Architecture: "arch"} + + img := configBuilder.VulnerableConfig(ispec.Image{ + Platform: ispec.Platform{OS: "os", Architecture: "arch"}, + }).Build() + + So(img.Layers[0], ShouldEqual, vulnLayer) + So(img.Config.Platform, ShouldResemble, platform) + }) + + Convey("Random VulnerableConfig", func() { + img := configBuilder.RandomVulnConfig().Build() + + So(img.Layers[0], ShouldEqual, vulnLayer) + }) + }) + + Convey("Manifes builder", t, func() { + manifestBuilder := test.CreateImageWith().DefaultLayers().DefaultConfig() + + subject := ispec.Descriptor{ + Digest: godigest.FromString("digest"), + MediaType: ispec.MediaTypeImageManifest, + } + + image := manifestBuilder. + Subject(&subject). + ArtifactType("art.type"). + Annotations(map[string]string{"key": "val"}). + Build() + + So(image.Layers, ShouldResemble, test.GetDefaultLayersBlobs()) + So(image.Config, ShouldResemble, test.GetDefaultConfig()) + So(image.Manifest.Subject, ShouldResemble, &subject) + So(image.Manifest.ArtifactType, ShouldResemble, "art.type") + So(image.Manifest.Annotations, ShouldResemble, map[string]string{"key": "val"}) + }) +} + +func TestMultiarchImageBuilder(t *testing.T) { + Convey("Multiarch", t, func() { + multiArch := test.CreateMultiarchWith(). + Images([]test.Image{ + test.CreateRandomImage(), + test.CreateRandomImage(), + }). + Annotations(map[string]string{"a": "b"}). + ArtifactType("art.type"). + Subject(&ispec.Descriptor{}). + Build() + + So(len(multiArch.Images), ShouldEqual, 2) + So(multiArch.Index.ArtifactType, ShouldResemble, "art.type") + So(multiArch.Index.Subject, ShouldNotBeNil) + So(multiArch.Index.Annotations, ShouldNotBeNil) + So(multiArch.Index.Annotations, ShouldContainKey, "a") + }) +} + +func TestPredefinedImages(t *testing.T) { + Convey("Predefined Images", t, func() { + img := test.CreateDefaultImage() + So(img.Layers, ShouldResemble, test.GetDefaultLayersBlobs()) + + img = test.CreateDefaultImageWith().ArtifactType("art.type").Build() + So(img.Manifest.ArtifactType, ShouldEqual, "art.type") + + img = test.CreateRandomImageWith().ArtifactType("art.type").Build() + So(img.Manifest.ArtifactType, ShouldEqual, "art.type") + + img = test.CreateRandomVulnerableImage() + So(img.Layers, ShouldNotResemble, test.GetDefaultLayersBlobs()) + + img = test.CreateRandomVulnerableImageWith().ArtifactType("art.type").Build() + So(img.Manifest.ArtifactType, ShouldEqual, "art.type") + }) + + Convey("Predefined Multiarch-Images", t, func() { + multiArch := test.CreateRandomMultiarch() + So(len(multiArch.Images), ShouldEqual, 3) + So(multiArch.Reference, ShouldResemble, multiArch.Digest().String()) + + multiArch = test.CreateVulnerableMultiarch() + So(len(multiArch.Images), ShouldEqual, 3) + So(multiArch.Reference, ShouldResemble, multiArch.Digest().String()) + }) +} + +func TestImageMethods(t *testing.T) { + img := test.CreateDefaultImage() + + Convey("Image", t, func() { + manifestBlob, err := json.Marshal(img.Manifest) + So(err, ShouldBeNil) + + manifestDigest := godigest.FromBytes(manifestBlob) + manifestSize := int64(len(manifestBlob)) + + Convey("img descriptor", func() { + descriptor := img.Descriptor() + + So(manifestDigest, ShouldResemble, descriptor.Digest) + So(manifestSize, ShouldEqual, descriptor.Size) + So(ispec.MediaTypeImageManifest, ShouldResemble, descriptor.MediaType) + }) + }) +} diff --git a/pkg/test/multiarch.go b/pkg/test/multiarch.go new file mode 100644 index 00000000..12519632 --- /dev/null +++ b/pkg/test/multiarch.go @@ -0,0 +1,147 @@ +package test + +import ( + "encoding/json" + + godigest "github.com/opencontainers/go-digest" + ispec "github.com/opencontainers/image-spec/specs-go/v1" + + mTypes "zotregistry.io/zot/pkg/meta/types" +) + +type MultiarchImage struct { + Index ispec.Index + Images []Image + Reference string + + indexDescriptor ispec.Descriptor +} + +func (mi *MultiarchImage) Digest() godigest.Digest { + indexBlob, err := json.Marshal(mi.Index) + if err != nil { + panic("unreachable: ispec.Index should always be marshable") + } + + return godigest.FromBytes(indexBlob) +} + +func (mi *MultiarchImage) DigestStr() string { + return mi.Digest().String() +} + +func (mi *MultiarchImage) IndexData() mTypes.IndexData { + indexBlob, err := json.Marshal(mi.Index) + if err != nil { + panic("unreachable: ispec.Index should always be marshable") + } + + return mTypes.IndexData{IndexBlob: indexBlob} +} + +type ImagesBuilder interface { + Images(images []Image) MultiarchBuilder +} + +type MultiarchBuilder interface { + Subject(subject *ispec.Descriptor) MultiarchBuilder + ArtifactType(artifactType string) MultiarchBuilder + Annotations(annotations map[string]string) MultiarchBuilder + Build() MultiarchImage +} + +func CreateMultiarchWith() ImagesBuilder { + return &BaseMultiarchBuilder{} +} + +func CreateRandomMultiarch() MultiarchImage { + return CreateMultiarchWith(). + Images([]Image{ + CreateRandomImage(), + CreateRandomImage(), + CreateRandomImage(), + }). + Build() +} + +func CreateVulnerableMultiarch() MultiarchImage { + return CreateMultiarchWith(). + Images([]Image{ + CreateRandomImage(), + CreateRandomVulnerableImage(), + CreateRandomImage(), + }). + Build() +} + +type BaseMultiarchBuilder struct { + images []Image + subject *ispec.Descriptor + artifactType string + annotations map[string]string +} + +func (mb *BaseMultiarchBuilder) Images(images []Image) MultiarchBuilder { + mb.images = images + + return mb +} + +func (mb *BaseMultiarchBuilder) Subject(subject *ispec.Descriptor) MultiarchBuilder { + mb.subject = subject + + return mb +} + +func (mb *BaseMultiarchBuilder) ArtifactType(artifactType string) MultiarchBuilder { + mb.artifactType = artifactType + + return mb +} + +func (mb *BaseMultiarchBuilder) Annotations(annotations map[string]string) MultiarchBuilder { + mb.annotations = annotations + + return mb +} + +func (mb *BaseMultiarchBuilder) Build() MultiarchImage { + manifests := make([]ispec.Descriptor, len(mb.images)) + + for i := range manifests { + manifests[i] = ispec.Descriptor{ + Digest: mb.images[i].ManifestDescriptor.Digest, + MediaType: ispec.MediaTypeImageManifest, + } + } + + index := ispec.Index{ + MediaType: ispec.MediaTypeImageIndex, + Manifests: manifests, + Annotations: mb.annotations, + Subject: mb.subject, + ArtifactType: mb.artifactType, + } + + indexBlob, err := json.Marshal(index) + if err != nil { + panic("unreachable: ispec.Index should always be marshable") + } + + indexDigest := godigest.FromBytes(indexBlob) + + ref := indexDigest.String() + + return MultiarchImage{ + Index: index, + Images: mb.images, + Reference: ref, + + indexDescriptor: ispec.Descriptor{ + MediaType: ispec.MediaTypeImageIndex, + Size: int64(len(indexBlob)), + Digest: indexDigest, + Data: indexBlob, + }, + } +}