diff --git a/pkg/extensions/search/digest/digest.go b/pkg/extensions/search/digest/digest.go deleted file mode 100644 index c5d9574e..00000000 --- a/pkg/extensions/search/digest/digest.go +++ /dev/null @@ -1,90 +0,0 @@ -package digestinfo - -import ( - "strings" - - godigest "github.com/opencontainers/go-digest" - ispec "github.com/opencontainers/image-spec/specs-go/v1" - - "zotregistry.io/zot/pkg/extensions/search/common" - "zotregistry.io/zot/pkg/log" - "zotregistry.io/zot/pkg/storage" -) - -// DigestInfo implements searching by manifes/config/layer digest. -type DigestInfo struct { - Log log.Logger - LayoutUtils *common.BaseOciLayoutUtils -} - -type ImageInfoByDigest struct { - Tag string - Digest godigest.Digest - Manifest ispec.Manifest -} - -// NewDigestInfo initializes a new DigestInfo object. -func NewDigestInfo(storeController storage.StoreController, log log.Logger) *DigestInfo { - layoutUtils := common.NewBaseOciLayoutUtils(storeController, log) - - return &DigestInfo{Log: log, LayoutUtils: layoutUtils} -} - -// FilterImagesByDigest returns a list of image tags in a repository matching a specific divest. -func (digestinfo DigestInfo) GetImageTagsByDigest(repo, digest string) ([]ImageInfoByDigest, error) { - imageTags := []ImageInfoByDigest{} - - manifests, err := digestinfo.LayoutUtils.GetImageManifests(repo) - if err != nil { - digestinfo.Log.Error().Err(err).Msg("unable to read image manifests") - - return imageTags, err - } - - for _, manifest := range manifests { - imageDigest := manifest.Digest - - val, ok := manifest.Annotations[ispec.AnnotationRefName] - if ok { - imageBlobManifest, err := digestinfo.LayoutUtils.GetImageBlobManifest(repo, imageDigest) - if err != nil { - digestinfo.Log.Error().Err(err).Msg("unable to read image blob manifest") - - return imageTags, err - } - - tags := []*string{} - - // Check the image manifest in index.json matches the search digest - // This is a blob with mediaType application/vnd.oci.image.manifest.v1+json - if strings.Contains(manifest.Digest.String(), digest) { - tags = append(tags, &val) - } - - // Check the image config matches the search digest - // This is a blob with mediaType application/vnd.oci.image.config.v1+json - if strings.Contains(imageBlobManifest.Config.Digest.String(), digest) { - tags = append(tags, &val) - } - - // Check to see if the individual layers in the oci image manifest match the digest - // These are blobs with mediaType application/vnd.oci.image.layer.v1.tar+gzip - for _, layer := range imageBlobManifest.Layers { - if strings.Contains(layer.Digest.String(), digest) { - tags = append(tags, &val) - } - } - - keys := make(map[string]bool) - - for _, entry := range tags { - if _, value := keys[*entry]; !value { - imageTags = append(imageTags, ImageInfoByDigest{Tag: *entry, Digest: imageDigest, Manifest: imageBlobManifest}) - keys[*entry] = true - } - } - } - } - - return imageTags, nil -} diff --git a/pkg/extensions/search/digest/digest_test.go b/pkg/extensions/search/digest_test.go similarity index 74% rename from pkg/extensions/search/digest/digest_test.go rename to pkg/extensions/search/digest_test.go index e361bee1..0dafd9d9 100644 --- a/pkg/extensions/search/digest/digest_test.go +++ b/pkg/extensions/search/digest_test.go @@ -1,15 +1,17 @@ //go:build search // +build search -//nolint:gochecknoinits -package digestinfo_test +package search_test import ( "encoding/json" "net/url" "os" "testing" + "time" + godigest "github.com/opencontainers/go-digest" + ispec "github.com/opencontainers/image-spec/specs-go/v1" . "github.com/smartystreets/goconvey/convey" "gopkg.in/resty.v1" @@ -17,12 +19,7 @@ import ( "zotregistry.io/zot/pkg/api/config" "zotregistry.io/zot/pkg/api/constants" extconf "zotregistry.io/zot/pkg/extensions/config" - "zotregistry.io/zot/pkg/extensions/monitoring" - digestinfo "zotregistry.io/zot/pkg/extensions/search/digest" - "zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/meta/repodb" - "zotregistry.io/zot/pkg/storage" - "zotregistry.io/zot/pkg/storage/local" . "zotregistry.io/zot/pkg/test" ) @@ -55,80 +52,9 @@ type PaginatedImagesResult struct { Page repodb.PageInfo `json:"page"` } -func testSetup(t *testing.T) (string, string, *digestinfo.DigestInfo) { - t.Helper() - dir := t.TempDir() - subDir := t.TempDir() - - rootDir := dir - - subRootDir := subDir - - // Test images used/copied: - // IMAGE NAME TAG DIGEST OS/ARCH CONFIG LAYERS SIZE - // zot-test 0.0.1 2bacca16 linux/amd64 adf3bb6c 76MB - // 2d473b07 76MB - // zot-cve-test 0.0.1 63a795ca linux/amd64 8dd57e17 75MB - // 7a0437f0 75MB - - err := os.Mkdir(subDir+"/a", 0o700) - if err != nil { - panic(err) - } - - CopyTestFiles("../../../../test/data", rootDir) - - CopyTestFiles("../../../../test/data", subDir+"/a/") - - log := log.NewLogger("debug", "") - metrics := monitoring.NewMetricsServer(false, log) - storeController := storage.StoreController{ - DefaultStore: local.NewImageStore(rootDir, false, storage.DefaultGCDelay, false, false, log, metrics, nil, nil), - } - - digestInfo := digestinfo.NewDigestInfo(storeController, log) - - return rootDir, subRootDir, digestInfo -} - -func TestDigestInfo(t *testing.T) { - Convey("Test image tag", t, func() { - _, _, digestInfo := testSetup(t) - - // Search by manifest digest - imageTags, err := digestInfo.GetImageTagsByDigest("zot-cve-test", - GetTestBlobDigest("zot-cve-test", "manifest").Encoded()) - So(err, ShouldBeNil) - So(len(imageTags), ShouldEqual, 1) - So(imageTags[0].Tag, ShouldEqual, "0.0.1") - - // Search by config digest - imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", GetTestBlobDigest("zot-test", "config").Encoded()) - So(err, ShouldBeNil) - So(len(imageTags), ShouldEqual, 1) - So(imageTags[0].Tag, ShouldEqual, "0.0.1") - - // Search by layer digest - imageTags, err = digestInfo.GetImageTagsByDigest("zot-cve-test", GetTestBlobDigest("zot-cve-test", "layer").Encoded()) - So(err, ShouldBeNil) - So(len(imageTags), ShouldEqual, 1) - So(imageTags[0].Tag, ShouldEqual, "0.0.1") - - // Search by non-existent image - imageTags, err = digestInfo.GetImageTagsByDigest("zot-tes", GetTestBlobDigest("zot-test", "manifest").Encoded()) - So(err, ShouldNotBeNil) - So(len(imageTags), ShouldEqual, 0) - - // Search by non-existent digest - imageTags, err = digestInfo.GetImageTagsByDigest("zot-test", "111") - So(err, ShouldBeNil) - So(len(imageTags), ShouldEqual, 0) - }) -} - func TestDigestSearchHTTP(t *testing.T) { Convey("Test image search by digest scanning", t, func() { - rootDir, _, _ := testSetup(t) + rootDir := t.TempDir() port := GetFreePort() baseURL := GetBaseURL(port) @@ -148,6 +74,58 @@ func TestDigestSearchHTTP(t *testing.T) { // shut down server defer ctrlManager.StopServer() + createdTime1 := time.Date(2009, 1, 1, 12, 0, 0, 0, time.UTC) + layers1 := [][]byte{ + {3, 2, 2}, + } + image1, err := GetImageWithComponents( + ispec.Image{ + Created: &createdTime1, + History: []ispec.History{ + { + Created: &createdTime1, + }, + }, + }, + layers1, + ) + So(err, ShouldBeNil) + + image1.Reference = "0.0.1" + err = UploadImage( + image1, + baseURL, + "zot-cve-test", + ) + So(err, ShouldBeNil) + + createdTime2 := time.Date(2010, 1, 1, 12, 0, 0, 0, time.UTC) + image2, err := GetImageWithComponents( + ispec.Image{ + History: []ispec.History{{Created: &createdTime2}}, + Platform: ispec.Platform{ + Architecture: "amd64", + OS: "linux", + }, + }, + [][]byte{ + {0, 0, 2}, + }, + ) + So(err, ShouldBeNil) + + image2.Reference = "0.0.1" + manifestDigest, err := image2.Digest() + So(err, ShouldBeNil) + + err = UploadImage(image2, baseURL, "zot-test") + So(err, ShouldBeNil) + + configBlob, err := json.Marshal(image2.Config) + So(err, ShouldBeNil) + + configDigest := godigest.FromBytes(configBlob) + resp, err := resty.R().Get(baseURL + "/v2/") So(resp, ShouldNotBeNil) So(err, ShouldBeNil) @@ -188,7 +166,7 @@ func TestDigestSearchHTTP(t *testing.T) { // Call should return {"data":{"ImageListForDigest":[{"Name":"zot-test","Tags":["0.0.1"]}]}} // GetTestBlobDigest("zot-test", "manifest").Encoded() should match the manifest of 1 image - gqlQuery := url.QueryEscape(`{ImageListForDigest(id:"` + GetTestBlobDigest("zot-test", "manifest").Encoded() + `") + gqlQuery := url.QueryEscape(`{ImageListForDigest(id:"` + manifestDigest.Encoded() + `") {Results{RepoName Tag Manifests {Digest ConfigDigest Size Layers { Digest }}}}}`) targetURL := baseURL + constants.FullSearchPrefix + `?query=` + gqlQuery @@ -205,8 +183,7 @@ func TestDigestSearchHTTP(t *testing.T) { So(responseStruct.ImgListForDigest.Results[0].RepoName, ShouldEqual, "zot-test") So(responseStruct.ImgListForDigest.Results[0].Tag, ShouldEqual, "0.0.1") - // GetTestBlobDigest("zot-test", "config").Encoded() should match the config of 1 image. - gqlQuery = url.QueryEscape(`{ImageListForDigest(id:"` + GetTestBlobDigest("zot-test", "config").Encoded() + `") + gqlQuery = url.QueryEscape(`{ImageListForDigest(id:"` + configDigest.Encoded() + `") {Results{RepoName Tag Manifests {Digest ConfigDigest Size Layers { Digest }}}}}`) targetURL = baseURL + constants.FullSearchPrefix + `?query=` + gqlQuery @@ -225,7 +202,8 @@ func TestDigestSearchHTTP(t *testing.T) { // Call should return {"data":{"ImageListForDigest":[{"Name":"zot-cve-test","Tags":["0.0.1"]}]}} // GetTestBlobDigest("zot-cve-test", "layer").Encoded() should match the layer of 1 image - gqlQuery = url.QueryEscape(`{ImageListForDigest(id:"` + GetTestBlobDigest("zot-cve-test", "layer").Encoded() + `") + layerDigest1 := godigest.FromBytes((layers1[0])) + gqlQuery = url.QueryEscape(`{ImageListForDigest(id:"` + layerDigest1.Encoded() + `") {Results{RepoName Tag Manifests {Digest ConfigDigest Size Layers { Digest }}}}}`) targetURL = baseURL + constants.FullSearchPrefix + `?query=` + gqlQuery @@ -293,7 +271,7 @@ func TestDigestSearchHTTP(t *testing.T) { func TestDigestSearchHTTPSubPaths(t *testing.T) { Convey("Test image search by digest scanning using storage subpaths", t, func() { - _, subRootDir, _ := testSetup(t) + subRootDir := t.TempDir() port := GetFreePort() baseURL := GetBaseURL(port) @@ -323,6 +301,17 @@ func TestDigestSearchHTTPSubPaths(t *testing.T) { // shut down server defer ctrlManager.StopServer() + config, layers, manifest, err := GetImageComponents(100) + So(err, ShouldBeNil) + + err = PushTestImage("a/zot-cve-test", "0.0.1", baseURL, + manifest, config, layers) + So(err, ShouldBeNil) + + err = PushTestImage("a/zot-test", "0.0.1", baseURL, + manifest, config, layers) + So(err, ShouldBeNil) + resp, err := resty.R().Get(baseURL + "/v2/") So(resp, ShouldNotBeNil) So(err, ShouldBeNil) diff --git a/pkg/extensions/search/resolver.go b/pkg/extensions/search/resolver.go index d6b73e1e..ea0eb018 100644 --- a/pkg/extensions/search/resolver.go +++ b/pkg/extensions/search/resolver.go @@ -21,7 +21,6 @@ import ( "zotregistry.io/zot/pkg/extensions/search/common" "zotregistry.io/zot/pkg/extensions/search/convert" cveinfo "zotregistry.io/zot/pkg/extensions/search/cve" - digestinfo "zotregistry.io/zot/pkg/extensions/search/digest" "zotregistry.io/zot/pkg/extensions/search/gql_generated" "zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/meta/repodb" @@ -38,7 +37,6 @@ type Resolver struct { cveInfo cveinfo.CveInfo repoDB repodb.RepoDB storeController storage.StoreController - digestInfo *digestinfo.DigestInfo log log.Logger } @@ -46,13 +44,10 @@ type Resolver struct { func GetResolverConfig(log log.Logger, storeController storage.StoreController, repoDB repodb.RepoDB, cveInfo cveinfo.CveInfo, ) gql_generated.Config { - digestInfo := digestinfo.NewDigestInfo(storeController, log) - resConfig := &Resolver{ cveInfo: cveInfo, repoDB: repoDB, storeController: storeController, - digestInfo: digestInfo, log: log, } @@ -65,13 +60,10 @@ func GetResolverConfig(log log.Logger, storeController storage.StoreController, func NewResolver(log log.Logger, storeController storage.StoreController, repoDB repodb.RepoDB, cveInfo cveinfo.CveInfo, ) *Resolver { - digestInfo := digestinfo.NewDigestInfo(storeController, log) - resolver := &Resolver{ cveInfo: cveInfo, repoDB: repoDB, storeController: storeController, - digestInfo: digestInfo, log: log, }