From 7e3d063319d048585b728ffeeafa3cb2eccb4ce1 Mon Sep 17 00:00:00 2001 From: Laurentiu Niculae Date: Tue, 12 Jul 2022 15:58:04 +0300 Subject: [PATCH] freeform querry api Signed-off-by: Laurentiu Niculae --- codecov.yml | 1 + go.mod | 8 +- go.sum | 14 +- pkg/api/routes_test.go | 552 ++---- pkg/extensions/search/common/common_test.go | 452 ++++- pkg/extensions/search/common/oci_layout.go | 146 +- pkg/extensions/search/cve/cve.go | 2 +- pkg/extensions/search/cve/cve_test.go | 2 +- pkg/extensions/search/cve/models.go | 2 +- pkg/extensions/search/digest/digest.go | 4 +- .../search/gql_generated/generated.go | 1707 +++++++++++++++++ .../search/gql_generated/models_gen.go | 37 + pkg/extensions/search/resolver.go | 178 +- pkg/extensions/search/resolver_test.go | 154 ++ pkg/extensions/search/schema.graphql | 45 + pkg/extensions/search/schema.resolvers.go | 31 +- pkg/storage/s3/s3_test.go | 228 +-- pkg/test/mocks/image_store_mock.go | 289 +++ pkg/test/mocks/oci_mock.go | 121 ++ pkg/test/mocks/storage_driver_mock.go | 186 ++ 20 files changed, 3597 insertions(+), 562 deletions(-) create mode 100644 pkg/extensions/search/resolver_test.go create mode 100644 pkg/test/mocks/image_store_mock.go create mode 100644 pkg/test/mocks/oci_mock.go create mode 100644 pkg/test/mocks/storage_driver_mock.go diff --git a/codecov.yml b/codecov.yml index 602ea0b9..55a89d82 100644 --- a/codecov.yml +++ b/codecov.yml @@ -4,3 +4,4 @@ ignore: - "./pkg/extensions/minimal.go" - "./pkg/cli/minimal.go" - "./cmd/zb/*.go" + - "./pkg/test/mocks/*.go" diff --git a/go.mod b/go.mod index e0f71429..93189245 100644 --- a/go.mod +++ b/go.mod @@ -361,16 +361,16 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect - golang.org/x/tools v0.1.10 // indirect - golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect + golang.org/x/tools v0.1.11 // indirect + golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/api v0.81.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect diff --git a/go.sum b/go.sum index d67b54f0..4a6b9a79 100644 --- a/go.sum +++ b/go.sum @@ -141,8 +141,6 @@ github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtE github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU= -github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU= -github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= @@ -2792,8 +2790,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -3099,8 +3098,9 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3241,15 +3241,17 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= +golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= diff --git a/pkg/api/routes_test.go b/pkg/api/routes_test.go index ac8cd081..b287f246 100644 --- a/pkg/api/routes_test.go +++ b/pkg/api/routes_test.go @@ -11,12 +11,9 @@ import ( "net/http" "net/http/httptest" "testing" - "time" "github.com/gorilla/mux" - "github.com/opencontainers/go-digest" ispec "github.com/opencontainers/image-spec/specs-go/v1" - artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1" . "github.com/smartystreets/goconvey/convey" zerr "zotregistry.io/zot/errors" "zotregistry.io/zot/pkg/api" @@ -24,290 +21,11 @@ import ( "zotregistry.io/zot/pkg/api/constants" "zotregistry.io/zot/pkg/storage" "zotregistry.io/zot/pkg/test" + "zotregistry.io/zot/pkg/test/mocks" ) var ErrUnexpectedError = errors.New("error: unexpected error") -type MockedImageStore struct { - dirExistsFn func(d string) bool - rootDirFn func() string - initRepoFn func(name string) error - validateRepoFn func(name string) (bool, error) - getRepositoriesFn func() ([]string, error) - getImageTagsFn func(repo string) ([]string, error) - getImageManifestFn func(repo string, reference string) ([]byte, string, string, error) - putImageManifestFn func(repo string, reference string, mediaType string, body []byte) (string, error) - deleteImageManifestFn func(repo string, reference string) error - blobUploadPathFn func(repo string, uuid string) string - newBlobUploadFn func(repo string) (string, error) - getBlobUploadFn func(repo string, uuid string) (int64, error) - blobUploadInfoFn func(repo string, uuid string) (int64, error) - putBlobChunkStreamedFn func(repo string, uuid string, body io.Reader) (int64, error) - putBlobChunkFn func(repo string, uuid string, from int64, to int64, body io.Reader) (int64, error) - finishBlobUploadFn func(repo string, uuid string, body io.Reader, digest string) error - fullBlobUploadFn func(repo string, body io.Reader, digest string) (string, int64, error) - dedupeBlobFn func(src string, dstDigest digest.Digest, dst string) error - deleteBlobUploadFn func(repo string, uuid string) error - blobPathFn func(repo string, digest digest.Digest) string - checkBlobFn func(repo string, digest string) (bool, int64, error) - getBlobFn func(repo string, digest string, mediaType string) (io.Reader, int64, error) - deleteBlobFn func(repo string, digest string) error - getIndexContentFn func(repo string) ([]byte, error) - getBlobContentFn func(repo, digest string) ([]byte, error) - getReferrersFn func(repo, digest string, mediaType string) ([]artifactspec.Descriptor, error) - urlForPathFn func(path string) (string, error) - runGCRepoFn func(repo string) -} - -func (is *MockedImageStore) Lock(t *time.Time) { -} - -func (is *MockedImageStore) Unlock(t *time.Time) { -} - -func (is *MockedImageStore) RUnlock(t *time.Time) { -} - -func (is *MockedImageStore) RLock(t *time.Time) { -} - -func (is *MockedImageStore) DirExists(d string) bool { - if is != nil && is.dirExistsFn != nil { - return is.dirExistsFn(d) - } - - return true -} - -func (is *MockedImageStore) RootDir() string { - if is != nil && is.rootDirFn != nil { - return is.rootDirFn() - } - - return "" -} - -func (is *MockedImageStore) InitRepo(name string) error { - if is != nil && is.initRepoFn != nil { - return is.initRepoFn(name) - } - - return nil -} - -func (is *MockedImageStore) ValidateRepo(name string) (bool, error) { - if is != nil && is.validateRepoFn != nil { - return is.validateRepoFn(name) - } - - return true, nil -} - -func (is *MockedImageStore) GetRepositories() ([]string, error) { - if is != nil && is.getRepositoriesFn != nil { - return is.getRepositoriesFn() - } - - return []string{}, nil -} - -func (is *MockedImageStore) GetImageManifest(repo string, reference string) ([]byte, string, string, error) { - if is != nil && is.getImageManifestFn != nil { - return is.getImageManifestFn(repo, reference) - } - - return []byte{}, "", "", nil -} - -func (is *MockedImageStore) PutImageManifest( - repo string, - reference string, - mediaType string, - body []byte, -) (string, error) { - if is != nil && is.putImageManifestFn != nil { - return is.putImageManifestFn(repo, reference, mediaType, body) - } - - return "", nil -} - -func (is *MockedImageStore) GetImageTags(name string) ([]string, error) { - if is != nil && is.getImageTagsFn != nil { - return is.getImageTagsFn(name) - } - - return []string{}, nil -} - -func (is *MockedImageStore) DeleteImageManifest(name string, reference string) error { - if is != nil && is.deleteImageManifestFn != nil { - return is.deleteImageManifestFn(name, reference) - } - - return nil -} - -func (is *MockedImageStore) NewBlobUpload(repo string) (string, error) { - if is != nil && is.newBlobUploadFn != nil { - return is.newBlobUploadFn(repo) - } - - return "", nil -} - -func (is *MockedImageStore) GetBlobUpload(repo string, uuid string) (int64, error) { - if is != nil && is.getBlobUploadFn != nil { - return is.getBlobUploadFn(repo, uuid) - } - - return 0, nil -} - -func (is *MockedImageStore) BlobUploadInfo(repo string, uuid string) (int64, error) { - if is != nil && is.blobUploadInfoFn != nil { - return is.blobUploadInfoFn(repo, uuid) - } - - return 0, nil -} - -func (is *MockedImageStore) BlobUploadPath(repo string, uuid string) string { - if is != nil && is.blobUploadPathFn != nil { - return is.blobUploadPathFn(repo, uuid) - } - - return "" -} - -func (is *MockedImageStore) PutBlobChunkStreamed(repo string, uuid string, body io.Reader) (int64, error) { - if is != nil && is.putBlobChunkStreamedFn != nil { - return is.putBlobChunkStreamedFn(repo, uuid, body) - } - - return 0, nil -} - -func (is *MockedImageStore) PutBlobChunk( - repo string, - uuid string, - from int64, - to int64, - body io.Reader, -) (int64, error) { - if is != nil && is.putBlobChunkFn != nil { - return is.putBlobChunkFn(repo, uuid, from, to, body) - } - - return 0, nil -} - -func (is *MockedImageStore) FinishBlobUpload(repo string, uuid string, body io.Reader, digest string) error { - if is != nil && is.finishBlobUploadFn != nil { - return is.finishBlobUploadFn(repo, uuid, body, digest) - } - - return nil -} - -func (is *MockedImageStore) FullBlobUpload(repo string, body io.Reader, digest string) (string, int64, error) { - if is != nil && is.fullBlobUploadFn != nil { - return is.fullBlobUploadFn(repo, body, digest) - } - - return "", 0, nil -} - -func (is *MockedImageStore) DedupeBlob(src string, dstDigest digest.Digest, dst string) error { - if is != nil && is.dedupeBlobFn != nil { - return is.dedupeBlobFn(src, dstDigest, dst) - } - - return nil -} - -func (is *MockedImageStore) DeleteBlob(repo string, digest string) error { - if is != nil && is.deleteBlobFn != nil { - return is.deleteBlobFn(repo, digest) - } - - return nil -} - -func (is *MockedImageStore) BlobPath(repo string, digest digest.Digest) string { - if is != nil && is.blobPathFn != nil { - return is.blobPathFn(repo, digest) - } - - return "" -} - -func (is *MockedImageStore) CheckBlob(repo string, digest string) (bool, int64, error) { - if is != nil && is.checkBlobFn != nil { - return is.checkBlobFn(repo, digest) - } - - return true, 0, nil -} - -func (is *MockedImageStore) GetBlob(repo string, digest string, mediaType string) (io.Reader, int64, error) { - if is != nil && is.getBlobFn != nil { - return is.getBlobFn(repo, digest, mediaType) - } - - return &io.LimitedReader{}, 0, nil -} - -func (is *MockedImageStore) DeleteBlobUpload(repo string, digest string) error { - if is != nil && is.deleteBlobUploadFn != nil { - return is.deleteBlobUploadFn(repo, digest) - } - - return nil -} - -func (is *MockedImageStore) GetIndexContent(repo string) ([]byte, error) { - if is != nil && is.getIndexContentFn != nil { - return is.getIndexContentFn(repo) - } - - return []byte{}, nil -} - -func (is *MockedImageStore) GetBlobContent(repo string, digest string) ([]byte, error) { - if is != nil && is.getBlobContentFn != nil { - return is.getBlobContentFn(repo, digest) - } - - return []byte{}, nil -} - -func (is *MockedImageStore) GetReferrers( - repo string, - digest string, - mediaType string, -) ([]artifactspec.Descriptor, error) { - if is != nil && is.getReferrersFn != nil { - return is.getReferrersFn(repo, digest, mediaType) - } - - return []artifactspec.Descriptor{}, nil -} - -func (is *MockedImageStore) URLForPath(path string) (string, error) { - if is != nil && is.urlForPathFn != nil { - return is.urlForPathFn(path) - } - - return "", nil -} - -func (is *MockedImageStore) RunGCRepo(repo string) { - if is != nil && is.runGCRepoFn != nil { - is.runGCRepoFn(repo) - } -} - func TestRoutes(t *testing.T) { Convey("Make a new controller", t, func() { port := test.GetFreePort() @@ -336,8 +54,8 @@ func TestRoutes(t *testing.T) { Convey("Get manifest", func() { // overwrite controller storage - ctlr.StoreController.DefaultStore = &MockedImageStore{ - getImageManifestFn: func(repo string, reference string) ([]byte, string, string, error) { + ctlr.StoreController.DefaultStore = &mocks.MockedImageStore{ + GetImageManifestFn: func(repo string, reference string) ([]byte, string, string, error) { return []byte{}, "", "", zerr.ErrRepoBadVersion }, } @@ -359,7 +77,7 @@ func TestRoutes(t *testing.T) { }) Convey("UpdateManifest ", func() { - testUpdateManifest := func(urlVars map[string]string, ism *MockedImageStore) int { + testUpdateManifest := func(urlVars map[string]string, ism *mocks.MockedImageStore) int { ctlr.StoreController.DefaultStore = ism str := []byte("test") request, _ := http.NewRequestWithContext(context.TODO(), "PUT", baseURL, bytes.NewBuffer(str)) @@ -380,8 +98,8 @@ func TestRoutes(t *testing.T) { "name": "test", "reference": "reference", }, - &MockedImageStore{ - putImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { + &mocks.MockedImageStore{ + PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { return "", zerr.ErrRepoNotFound }, }) @@ -393,8 +111,8 @@ func TestRoutes(t *testing.T) { "reference": "reference", }, - &MockedImageStore{ - putImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { + &mocks.MockedImageStore{ + PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { return "", zerr.ErrManifestNotFound }, }) @@ -405,8 +123,8 @@ func TestRoutes(t *testing.T) { "name": "test", "reference": "reference", }, - &MockedImageStore{ - putImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { + &mocks.MockedImageStore{ + PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { return "", zerr.ErrBadManifest }, }) @@ -417,8 +135,8 @@ func TestRoutes(t *testing.T) { "name": "test", "reference": "reference", }, - &MockedImageStore{ - putImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { + &mocks.MockedImageStore{ + PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { return "", zerr.ErrBlobNotFound }, }) @@ -430,8 +148,8 @@ func TestRoutes(t *testing.T) { "name": "test", "reference": "reference", }, - &MockedImageStore{ - putImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { + &mocks.MockedImageStore{ + PutImageManifestFn: func(repo, reference, mediaType string, body []byte) (string, error) { return "", zerr.ErrRepoBadVersion }, }) @@ -439,7 +157,7 @@ func TestRoutes(t *testing.T) { }) Convey("DeleteManifest", func() { - testDeleteManifest := func(headers map[string]string, urlVars map[string]string, ism *MockedImageStore) int { + testDeleteManifest := func(headers map[string]string, urlVars map[string]string, ism *mocks.MockedImageStore) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.Background(), "DELETE", baseURL, nil) request = mux.SetURLVars(request, urlVars) @@ -463,8 +181,8 @@ func TestRoutes(t *testing.T) { "name": "ErrManifestNotFound", "reference": "reference", }, - &MockedImageStore{ - deleteImageManifestFn: func(repo, reference string) error { + &mocks.MockedImageStore{ + DeleteImageManifestFn: func(repo, reference string) error { return zerr.ErrRepoNotFound }, }, @@ -478,8 +196,8 @@ func TestRoutes(t *testing.T) { "name": "ErrManifestNotFound", "reference": "reference", }, - &MockedImageStore{ - deleteImageManifestFn: func(repo, reference string) error { + &mocks.MockedImageStore{ + DeleteImageManifestFn: func(repo, reference string) error { return zerr.ErrManifestNotFound }, }, @@ -493,8 +211,8 @@ func TestRoutes(t *testing.T) { "name": "ErrUnexpectedError", "reference": "reference", }, - &MockedImageStore{ - deleteImageManifestFn: func(repo, reference string) error { + &mocks.MockedImageStore{ + DeleteImageManifestFn: func(repo, reference string) error { return ErrUnexpectedError }, }, @@ -508,8 +226,8 @@ func TestRoutes(t *testing.T) { "name": "ErrBadManifest", "reference": "reference", }, - &MockedImageStore{ - deleteImageManifestFn: func(repo, reference string) error { + &mocks.MockedImageStore{ + DeleteImageManifestFn: func(repo, reference string) error { return zerr.ErrBadManifest }, }, @@ -518,7 +236,7 @@ func TestRoutes(t *testing.T) { }) Convey("DeleteBlob", func() { - testDeleteBlob := func(urlVars map[string]string, ism *MockedImageStore) int { + testDeleteBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "DELETE", baseURL, nil) request = mux.SetURLVars(request, urlVars) @@ -538,8 +256,8 @@ func TestRoutes(t *testing.T) { "name": "ErrUnexpectedError", "digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - deleteBlobFn: func(repo, digest string) error { + &mocks.MockedImageStore{ + DeleteBlobFn: func(repo, digest string) error { return ErrUnexpectedError }, }) @@ -550,8 +268,8 @@ func TestRoutes(t *testing.T) { "name": "ErrBadBlobDigest", "digest": "sha256:7b8437f04f83f084b7ed68ad8c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - deleteBlobFn: func(repo, digest string) error { + &mocks.MockedImageStore{ + DeleteBlobFn: func(repo, digest string) error { return zerr.ErrBadBlobDigest }, }) @@ -563,8 +281,8 @@ func TestRoutes(t *testing.T) { "name": "ErrBlobNotFound", "digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - deleteBlobFn: func(repo, digest string) error { + &mocks.MockedImageStore{ + DeleteBlobFn: func(repo, digest string) error { return zerr.ErrBlobNotFound }, }) @@ -576,8 +294,8 @@ func TestRoutes(t *testing.T) { "name": "ErrRepoNotFound", "digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - deleteBlobFn: func(repo, digest string) error { + &mocks.MockedImageStore{ + DeleteBlobFn: func(repo, digest string) error { return zerr.ErrRepoNotFound }, }) @@ -586,7 +304,7 @@ func TestRoutes(t *testing.T) { // Check Blob Convey("CheckBlob", func() { - testCheckBlob := func(urlVars map[string]string, ism *MockedImageStore) int { + testCheckBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "HEAD", baseURL, nil) request = mux.SetURLVars(request, urlVars) @@ -606,8 +324,8 @@ func TestRoutes(t *testing.T) { "name": "ErrBadBlobDigest", "digest": "1234", }, - &MockedImageStore{ - checkBlobFn: func(repo, digest string) (bool, int64, error) { + &mocks.MockedImageStore{ + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrBadBlobDigest }, }) @@ -619,8 +337,8 @@ func TestRoutes(t *testing.T) { "name": "ErrRepoNotFound", "digest": "1234", }, - &MockedImageStore{ - checkBlobFn: func(repo, digest string) (bool, int64, error) { + &mocks.MockedImageStore{ + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrRepoNotFound }, }) @@ -632,8 +350,8 @@ func TestRoutes(t *testing.T) { "name": "ErrBlobNotFound", "digest": "1234", }, - &MockedImageStore{ - checkBlobFn: func(repo, digest string) (bool, int64, error) { + &mocks.MockedImageStore{ + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrBlobNotFound }, }) @@ -645,8 +363,8 @@ func TestRoutes(t *testing.T) { "name": "ErrUnexpectedError", "digest": "1234", }, - &MockedImageStore{ - checkBlobFn: func(repo, digest string) (bool, int64, error) { + &mocks.MockedImageStore{ + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, ErrUnexpectedError }, }) @@ -658,8 +376,8 @@ func TestRoutes(t *testing.T) { "name": "Check Blob Not Ok", "digest": "1234", }, - &MockedImageStore{ - checkBlobFn: func(repo, digest string) (bool, int64, error) { + &mocks.MockedImageStore{ + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return false, 0, nil }, }) @@ -667,7 +385,7 @@ func TestRoutes(t *testing.T) { }) Convey("GetBlob", func() { - testGetBlob := func(urlVars map[string]string, ism *MockedImageStore) int { + testGetBlob := func(urlVars map[string]string, ism *mocks.MockedImageStore) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil) request = mux.SetURLVars(request, urlVars) @@ -686,8 +404,8 @@ func TestRoutes(t *testing.T) { "name": "ErrRepoNotFound", "digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - getBlobFn: func(repo, digest, mediaType string) (io.Reader, int64, error) { + &mocks.MockedImageStore{ + GetBlobFn: func(repo, digest, mediaType string) (io.Reader, int64, error) { return bytes.NewBuffer([]byte("")), 0, zerr.ErrRepoNotFound }, }) @@ -699,8 +417,8 @@ func TestRoutes(t *testing.T) { "name": "ErrRepoNotFound", "digest": "sha256:7a0437f04f83f084b7ed68ad9c4a4947e12fc4e1b006b38129bac89114ec3621", }, - &MockedImageStore{ - getBlobFn: func(repo, digest, mediaType string) (io.Reader, int64, error) { + &mocks.MockedImageStore{ + GetBlobFn: func(repo, digest, mediaType string) (io.Reader, int64, error) { return bytes.NewBuffer([]byte("")), 0, zerr.ErrBadBlobDigest }, }) @@ -711,7 +429,7 @@ func TestRoutes(t *testing.T) { testCreateBlobUpload := func( query []struct{ k, v string }, headers map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "POST", baseURL, nil) @@ -747,11 +465,11 @@ func TestRoutes(t *testing.T) { {"mount", "1234"}, }, map[string]string{}, - &MockedImageStore{ - newBlobUploadFn: func(repo string) (string, error) { + &mocks.MockedImageStore{ + NewBlobUploadFn: func(repo string) (string, error) { return "", zerr.ErrRepoNotFound }, - checkBlobFn: func(repo, digest string) (bool, int64, error) { + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrRepoNotFound }, }) @@ -764,11 +482,11 @@ func TestRoutes(t *testing.T) { {"digest", "5234"}, }, map[string]string{}, - &MockedImageStore{ - newBlobUploadFn: func(repo string) (string, error) { + &mocks.MockedImageStore{ + NewBlobUploadFn: func(repo string) (string, error) { return "", zerr.ErrRepoNotFound }, - checkBlobFn: func(repo, digest string) (bool, int64, error) { + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrRepoNotFound }, }) @@ -782,11 +500,11 @@ func TestRoutes(t *testing.T) { map[string]string{ "Content-Type": "badContentType", }, - &MockedImageStore{ - newBlobUploadFn: func(repo string) (string, error) { + &mocks.MockedImageStore{ + NewBlobUploadFn: func(repo string) (string, error) { return "", zerr.ErrRepoNotFound }, - checkBlobFn: func(repo, digest string) (bool, int64, error) { + CheckBlobFn: func(repo, digest string) (bool, int64, error) { return true, 0, zerr.ErrRepoNotFound }, }) @@ -801,8 +519,8 @@ func TestRoutes(t *testing.T) { "Content-Type": constants.BinaryMediaType, "Content-Length": "100", }, - &MockedImageStore{ - fullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) { + &mocks.MockedImageStore{ + FullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) { return "session", 0, zerr.ErrBadBlobDigest }, }) @@ -817,8 +535,8 @@ func TestRoutes(t *testing.T) { "Content-Type": constants.BinaryMediaType, "Content-Length": "100", }, - &MockedImageStore{ - fullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) { + &mocks.MockedImageStore{ + FullBlobUploadFn: func(repo string, body io.Reader, digest string) (string, int64, error) { return "session", 20, nil }, }) @@ -831,8 +549,8 @@ func TestRoutes(t *testing.T) { "Content-Type": constants.BinaryMediaType, "Content-Length": "100", }, - &MockedImageStore{ - newBlobUploadFn: func(repo string) (string, error) { + &mocks.MockedImageStore{ + NewBlobUploadFn: func(repo string) (string, error) { return "", zerr.ErrRepoNotFound }, }) @@ -845,8 +563,8 @@ func TestRoutes(t *testing.T) { "Content-Type": constants.BinaryMediaType, "Content-Length": "100", }, - &MockedImageStore{ - newBlobUploadFn: func(repo string) (string, error) { + &mocks.MockedImageStore{ + NewBlobUploadFn: func(repo string) (string, error) { return "", ErrUnexpectedError }, }) @@ -858,7 +576,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "GET", baseURL, nil) @@ -892,8 +610,8 @@ func TestRoutes(t *testing.T) { "name": "test", "session_id": "1234", }, - &MockedImageStore{ - getBlobUploadFn: func(repo, uuid string) (int64, error) { + &mocks.MockedImageStore{ + GetBlobUploadFn: func(repo, uuid string) (int64, error) { return 0, zerr.ErrBadUploadRange }, }) @@ -909,8 +627,8 @@ func TestRoutes(t *testing.T) { "name": "test", "session_id": "1234", }, - &MockedImageStore{ - getBlobUploadFn: func(repo, uuid string) (int64, error) { + &mocks.MockedImageStore{ + GetBlobUploadFn: func(repo, uuid string) (int64, error) { return 0, zerr.ErrBadBlobDigest }, }) @@ -926,8 +644,8 @@ func TestRoutes(t *testing.T) { "name": "test", "session_id": "1234", }, - &MockedImageStore{ - getBlobUploadFn: func(repo, uuid string) (int64, error) { + &mocks.MockedImageStore{ + GetBlobUploadFn: func(repo, uuid string) (int64, error) { return 0, zerr.ErrRepoNotFound }, }) @@ -943,8 +661,8 @@ func TestRoutes(t *testing.T) { "name": "test", "session_id": "1234", }, - &MockedImageStore{ - getBlobUploadFn: func(repo, uuid string) (int64, error) { + &mocks.MockedImageStore{ + GetBlobUploadFn: func(repo, uuid string) (int64, error) { return 0, zerr.ErrUploadNotFound }, }) @@ -960,8 +678,8 @@ func TestRoutes(t *testing.T) { "name": "test", "session_id": "1234", }, - &MockedImageStore{ - getBlobUploadFn: func(repo, uuid string) (int64, error) { + &mocks.MockedImageStore{ + GetBlobUploadFn: func(repo, uuid string) (int64, error) { return 0, ErrUnexpectedError }, }) @@ -973,7 +691,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil) @@ -1010,7 +728,7 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{}, + &mocks.MockedImageStore{}, ) So(status, ShouldEqual, http.StatusBadRequest) @@ -1024,7 +742,7 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{}, + &mocks.MockedImageStore{}, ) So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable) @@ -1038,8 +756,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 100, zerr.ErrRepoNotFound }, }, @@ -1056,8 +774,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 100, zerr.ErrUploadNotFound }, }, @@ -1074,8 +792,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 100, ErrUnexpectedError }, }, @@ -1088,7 +806,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil) @@ -1127,7 +845,7 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{}, + &mocks.MockedImageStore{}, ) So(status, ShouldEqual, http.StatusBadRequest) @@ -1143,7 +861,7 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{}, + &mocks.MockedImageStore{}, ) So(status, ShouldEqual, http.StatusRequestedRangeNotSatisfiable) @@ -1159,8 +877,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 0, zerr.ErrBadUploadRange }, }, @@ -1179,8 +897,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 0, zerr.ErrRepoNotFound }, }, @@ -1199,8 +917,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 0, zerr.ErrUploadNotFound }, }, @@ -1219,8 +937,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - putBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { + &mocks.MockedImageStore{ + PutBlobChunkFn: func(repo, uuid string, from, to int64, body io.Reader) (int64, error) { return 0, ErrUnexpectedError }, }, @@ -1239,8 +957,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrBadBlobDigest }, }, @@ -1259,8 +977,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrBadUploadRange }, }, @@ -1279,8 +997,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrRepoNotFound }, }, @@ -1299,8 +1017,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrUploadNotFound }, }, @@ -1319,8 +1037,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return ErrUnexpectedError }, }, @@ -1333,7 +1051,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil) @@ -1367,8 +1085,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - deleteBlobUploadFn: func(repo, uuid string) error { + &mocks.MockedImageStore{ + DeleteBlobUploadFn: func(repo, uuid string) error { return zerr.ErrRepoNotFound }, }, @@ -1382,8 +1100,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - deleteBlobUploadFn: func(repo, uuid string) error { + &mocks.MockedImageStore{ + DeleteBlobUploadFn: func(repo, uuid string) error { return zerr.ErrUploadNotFound }, }, @@ -1397,8 +1115,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - deleteBlobUploadFn: func(repo, uuid string) error { + &mocks.MockedImageStore{ + DeleteBlobUploadFn: func(repo, uuid string) error { return ErrUnexpectedError }, }, @@ -1411,12 +1129,12 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism ctlr.StoreController.SubStore = map[string]storage.ImageStore{ - "test": &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + "test": &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{}, ErrUnexpectedError }, }, @@ -1449,7 +1167,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism ctlr.StoreController.SubStore = map[string]storage.ImageStore{} @@ -1484,8 +1202,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{}, ErrUnexpectedError }, }, @@ -1499,8 +1217,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{}, ErrUnexpectedError }, }, @@ -1509,19 +1227,19 @@ func TestRoutes(t *testing.T) { }) Convey("ListRepositories with Authz", func() { - ctlr.StoreController.DefaultStore = &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + ctlr.StoreController.DefaultStore = &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{"repo"}, nil }, } ctlr.StoreController.SubStore = map[string]storage.ImageStore{ - "test1": &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + "test1": &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{"repo1"}, nil }, }, - "test2": &MockedImageStore{ - getRepositoriesFn: func() ([]string, error) { + "test2": &mocks.MockedImageStore{ + GetRepositoriesFn: func() ([]string, error) { return []string{"repo2"}, nil }, }, @@ -1551,7 +1269,7 @@ func TestRoutes(t *testing.T) { query []struct{ k, v string }, headers map[string]string, vars map[string]string, - ism *MockedImageStore, + ism *mocks.MockedImageStore, ) int { ctlr.StoreController.DefaultStore = ism request, _ := http.NewRequestWithContext(context.TODO(), "PATCH", baseURL, nil) @@ -1590,8 +1308,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrUploadNotFound }, }, @@ -1610,8 +1328,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrUploadNotFound }, }, @@ -1630,8 +1348,8 @@ func TestRoutes(t *testing.T) { "name": "repo", "session_id": "test", }, - &MockedImageStore{ - finishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { + &mocks.MockedImageStore{ + FinishBlobUploadFn: func(repo, uuid string, body io.Reader, digest string) error { return zerr.ErrUploadNotFound }, }, diff --git a/pkg/extensions/search/common/common_test.go b/pkg/extensions/search/common/common_test.go index a15c26f1..5d778fd3 100644 --- a/pkg/extensions/search/common/common_test.go +++ b/pkg/extensions/search/common/common_test.go @@ -6,8 +6,10 @@ package common_test import ( "context" "encoding/json" + "errors" "fmt" "io/ioutil" + "net/url" "os" "os/exec" "path" @@ -30,12 +32,15 @@ import ( "zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/storage" . "zotregistry.io/zot/pkg/test" + "zotregistry.io/zot/pkg/test/mocks" ) const ( graphqlQueryPrefix = constants.ExtSearchPrefix ) +var ErrTestError = errors.New("test error") + // nolint:gochecknoglobals var ( rootDir string @@ -52,6 +57,50 @@ type ExpandedRepoInfoResp struct { Errors []ErrorGQL `json:"errors"` } +type GlobalSearchResultResp struct { + GlobalSearchResult GlobalSearchResult `json:"data"` + Errors []ErrorGQL `json:"errors"` +} + +type GlobalSearchResult struct { + GlobalSearch GlobalSearch `json:"globalSearch"` +} +type GlobalSearch struct { + Images []ImageSummary `json:"images"` + Repos []RepoSummary `json:"repos"` + Layers []LayerSummary `json:"layers"` +} + +type ImageSummary struct { + RepoName string `json:"repoName"` + Tag string `json:"tag"` + LastUpdated time.Time `json:"lastUpdated"` + Size string `json:"size"` + Platform OsArch `json:"platform"` + Vendor string `json:"vendor"` + Score int `json:"score"` +} + +type RepoSummary struct { + Name string `json:"name"` + LastUpdated time.Time `json:"lastUpdated"` + Size string `json:"size"` + Platforms []OsArch `json:"platforms"` + Vendors []string `json:"vendors"` + Score int `json:"score"` +} + +type LayerSummary struct { + Size string `json:"size"` + Digest string `json:"digest"` + Score int `json:"score"` +} + +type OsArch struct { + Os string `json:"os"` + Arch string `json:"arch"` +} + type ExpandedRepoInfo struct { RepoInfo common.RepoInfo `json:"expandedRepoInfo"` } @@ -210,7 +259,7 @@ func TestImageFormat(t *testing.T) { metrics := monitoring.NewMetricsServer(false, log) defaultStore := storage.NewImageStore(dbDir, false, storage.DefaultGCDelay, false, false, log, metrics) storeController := storage.StoreController{DefaultStore: defaultStore} - olu := common.NewOciLayoutUtils(storeController, log) + olu := common.NewBaseOciLayoutUtils(storeController, log) isValidImage, err := olu.IsValidImageFormat("zot-test") So(err, ShouldBeNil) @@ -671,3 +720,404 @@ func TestUtilsMethod(t *testing.T) { So(dir, ShouldEqual, subRootDir) }) } + +func TestGlobalSearch(t *testing.T) { + Convey("Test utils", t, func() { + subpath := "/a" + + err := testSetup(t, subpath) + if err != nil { + panic(err) + } + + port := GetFreePort() + baseURL := GetBaseURL(port) + conf := config.New() + conf.HTTP.Port = port + conf.Storage.RootDirectory = rootDir + conf.Storage.SubPaths = make(map[string]config.StorageConfig) + conf.Storage.SubPaths[subpath] = config.StorageConfig{RootDirectory: subRootDir} + defaultVal := true + conf.Extensions = &extconf.ExtensionConfig{ + Search: &extconf.SearchConfig{Enable: &defaultVal}, + } + + conf.Extensions.Search.CVE = nil + + ctlr := api.NewController(conf) + + go func() { + // this blocks + if err := ctlr.Run(context.Background()); err != nil { + return + } + }() + + // wait till ready + for { + _, err := resty.R().Get(baseURL) + if err == nil { + break + } + + time.Sleep(100 * time.Millisecond) + } + + // shut down server + + defer func() { + ctx := context.Background() + _ = ctlr.Server.Shutdown(ctx) + }() + + query := ` + { + GlobalSearch(query:""){ + Images { + RepoName + Tag + LastUpdated + Size + Score + } + Repos { + Name + LastUpdated + Size + Platforms { + Os + Arch + } + Vendors + Score + } + Layers { + Digest + Size + } + } + }` + resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct := &GlobalSearchResultResp{} + + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldNotBeNil) + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Images), ShouldNotBeEmpty) + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldNotBeEmpty) + So(len(responseStruct.GlobalSearchResult.GlobalSearch.Layers), ShouldNotBeEmpty) + + // GetRepositories fail + + err = os.Chmod(rootDir, 0o333) + So(err, ShouldBeNil) + + resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query)) + So(resp, ShouldNotBeNil) + So(err, ShouldBeNil) + So(resp.StatusCode(), ShouldEqual, 200) + + responseStruct = &GlobalSearchResultResp{} + err = json.Unmarshal(resp.Body(), responseStruct) + So(err, ShouldBeNil) + + So(responseStruct.Errors, ShouldNotBeEmpty) + err = os.Chmod(rootDir, 0o777) + So(err, ShouldBeNil) + }) +} + +func TestBaseOciLayoutUtils(t *testing.T) { + manifestDigest := "sha256:adf3bb6cc81f8bd6a9d5233be5f0c1a4f1e3ed1cf5bbdfad7708cc8d4099b741" + + Convey("GetImageManifestSize fail", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + size := olu.GetImageManifestSize("", "") + So(size, ShouldBeZeroValue) + }) + + Convey("GetImageConfigSize: fail GetImageBlobManifest", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + size := olu.GetImageConfigSize("", "") + So(size, ShouldBeZeroValue) + }) + + Convey("GetImageConfigSize: config GetBlobContent fail", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + if digest == manifestDigest { + return []byte{}, ErrTestError + } + + return []byte( + ` + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": manifestDigest, + "size": 1476 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc", + "size": 76097157 + } + ] + }`), nil + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + size := olu.GetImageConfigSize("", "") + So(size, ShouldBeZeroValue) + }) + + Convey("GetRepoLastUpdated: config GetBlobContent fail", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetIndexContentFn: func(repo string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + _, err := olu.GetRepoLastUpdated("") + So(err, ShouldNotBeNil) + }) + + Convey("GetImageLastUpdated: GetImageBlobManifest fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + time := olu.GetImageLastUpdated("", "") + So(time, ShouldBeZeroValue) + }) + + Convey("GetImageLastUpdated: GetImageInfo fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + if digest == manifestDigest { + return []byte{}, ErrTestError + } + + return []byte( + ` + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": manifestDigest, + "size": 1476 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc", + "size": 76097157 + } + ] + }`), nil + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + time := olu.GetImageLastUpdated("", "") + So(time, ShouldBeZeroValue) + }) + + Convey("GetImageLastUpdated: GetImageInfo history is empty", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + if digest == manifestDigest { + return []byte( + ` + { + "created": "2020-11-14T00:20:04.644613188Z", + "architecture": "amd64", + "os": "linux", + "config": { + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "Cmd": [ + "/bin/bash" + ], + "Labels": { + } + }, + "rootfs": { + "type": "layers", + "diff_ids": [ + "sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02" + ] + }, + "history": [ + ] + } + `), nil + } + + return []byte( + ` + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": manifestDigest, + "size": 1476 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc", + "size": 76097157 + } + ] + }`), nil + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + time := olu.GetImageLastUpdated("", "") + So(time, ShouldBeZeroValue) + }) + + Convey("GetImagePlatform: GetImageBlobManifest fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + os, arch := olu.GetImagePlatform("", "") + So(os, ShouldBeZeroValue) + So(arch, ShouldBeZeroValue) + }) + + Convey("GetImagePlatform: GetImageInfo fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + if digest == manifestDigest { + return []byte{}, ErrTestError + } + + return []byte( + ` + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": manifestDigest, + "size": 1476 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc", + "size": 76097157 + } + ] + }`), nil + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + os, arch := olu.GetImagePlatform("", "") + So(os, ShouldBeZeroValue) + So(arch, ShouldBeZeroValue) + }) + + Convey("GetImageVendor: GetImageBlobManifest fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + return []byte{}, ErrTestError + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + vendor := olu.GetImageVendor("", "") + So(vendor, ShouldBeZeroValue) + }) + + Convey("GetImageVendor: GetImageInfo fails", t, func() { + mockStoreController := mocks.MockedImageStore{ + GetBlobContentFn: func(repo, digest string) ([]byte, error) { + if digest == manifestDigest { + return []byte{}, ErrTestError + } + + return []byte( + ` + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": manifestDigest, + "size": 1476 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc", + "size": 76097157 + } + ] + }`), nil + }, + } + + storeController := storage.StoreController{DefaultStore: mockStoreController} + olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", "")) + + vendor := olu.GetImageVendor("", "") + So(vendor, ShouldBeZeroValue) + }) +} diff --git a/pkg/extensions/search/common/oci_layout.go b/pkg/extensions/search/common/oci_layout.go index b24bf20c..b450664b 100644 --- a/pkg/extensions/search/common/oci_layout.go +++ b/pkg/extensions/search/common/oci_layout.go @@ -20,8 +20,23 @@ import ( "zotregistry.io/zot/pkg/storage" ) +type OciLayoutUtils interface { + GetImageManifests(image string) ([]ispec.Descriptor, error) + GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) + GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) + IsValidImageFormat(image string) (bool, error) + GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) + GetImageLastUpdated(repo string, manifestDigest godigest.Digest) time.Time + GetImagePlatform(repo string, manifestDigest godigest.Digest) (string, string) + GetImageVendor(repo string, manifestDigest godigest.Digest) string + GetImageManifestSize(repo string, manifestDigest godigest.Digest) int64 + GetImageConfigSize(repo string, manifestDigest godigest.Digest) int64 + GetRepoLastUpdated(repo string) (time.Time, error) + GetExpandedRepoInfo(name string) (RepoInfo, error) +} + // OciLayoutInfo ... -type OciLayoutUtils struct { +type BaseOciLayoutUtils struct { Log log.Logger StoreController storage.StoreController } @@ -42,13 +57,13 @@ type Layer struct { Digest string `json:"digest"` } -// NewOciLayoutUtils initializes a new OciLayoutUtils object. -func NewOciLayoutUtils(storeController storage.StoreController, log log.Logger) *OciLayoutUtils { - return &OciLayoutUtils{Log: log, StoreController: storeController} +// NewBaseOciLayoutUtils initializes a new OciLayoutUtils object. +func NewBaseOciLayoutUtils(storeController storage.StoreController, log log.Logger) *BaseOciLayoutUtils { + return &BaseOciLayoutUtils{Log: log, StoreController: storeController} } // Below method will return image path including root dir, root dir is determined by splitting. -func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) { +func (olu BaseOciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) { imageStore := olu.StoreController.GetImageStore(image) buf, err := imageStore.GetIndexContent(image) @@ -76,7 +91,7 @@ func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, e } //nolint: interfacer -func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) { +func (olu BaseOciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) { var blobIndex v1.Manifest imageStore := olu.StoreController.GetImageStore(imageDir) @@ -98,7 +113,7 @@ func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest. } //nolint: interfacer -func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) { +func (olu BaseOciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) { var imageInfo ispec.Image imageStore := olu.StoreController.GetImageStore(imageDir) @@ -119,7 +134,7 @@ func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Ima return imageInfo, err } -func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) { +func (olu BaseOciLayoutUtils) IsValidImageFormat(image string) (bool, error) { imageDir, inputTag := GetImageDirAndTag(image) manifests, err := olu.GetImageManifests(imageDir) @@ -158,7 +173,7 @@ func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) { } // GetImageTagsWithTimestamp returns a list of image tags with timestamp available in the specified repository. -func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) { +func (olu BaseOciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) { tagsInfo := make([]TagInfo, 0) manifests, err := olu.GetImageManifests(repo) @@ -203,7 +218,7 @@ func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, err } // check notary signature corresponding to repo name, manifest digest and mediatype. -func (olu OciLayoutUtils) checkNotarySignature(name string, digest godigest.Digest) bool { +func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.Digest) bool { imageStore := olu.StoreController.GetImageStore(name) mediaType := notreg.ArtifactTypeNotation @@ -219,7 +234,7 @@ func (olu OciLayoutUtils) checkNotarySignature(name string, digest godigest.Dige } // check cosign signature corresponding to manifest. -func (olu OciLayoutUtils) checkCosignSignature(name string, digest godigest.Digest) bool { +func (olu BaseOciLayoutUtils) checkCosignSignature(name string, digest godigest.Digest) bool { imageStore := olu.StoreController.GetImageStore(name) // if manifest is signed using cosign mechanism, cosign adds a new manifest. @@ -240,7 +255,7 @@ func (olu OciLayoutUtils) checkCosignSignature(name string, digest godigest.Dige // checks if manifest is signed or not // checks for notary or cosign signature // if cosign signature found it does not looks for notary signature. -func (olu OciLayoutUtils) checkManifestSignature(name string, digest godigest.Digest) bool { +func (olu BaseOciLayoutUtils) checkManifestSignature(name string, digest godigest.Digest) bool { if !olu.checkCosignSignature(name, digest) { return olu.checkNotarySignature(name, digest) } @@ -248,7 +263,112 @@ func (olu OciLayoutUtils) checkManifestSignature(name string, digest godigest.Di return true } -func (olu OciLayoutUtils) GetExpandedRepoInfo(name string) (RepoInfo, error) { +func (olu BaseOciLayoutUtils) GetImageLastUpdated(repo string, manifestDigest godigest.Digest) time.Time { + imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest) + if err != nil { + olu.Log.Error().Err(err).Msg("unable to read image blob") + + return time.Time{} + } + + imageInfo, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest) + if err != nil { + olu.Log.Error().Err(err).Msg("unable to read image info") + + return time.Time{} + } + + var timeStamp time.Time + + if len(imageInfo.History) != 0 { + timeStamp = *imageInfo.History[0].Created + } else { + timeStamp = time.Time{} + } + + return timeStamp +} + +func (olu BaseOciLayoutUtils) GetImagePlatform(repo string, manifestDigest godigest.Digest) ( + string, string, +) { + imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest) + if err != nil { + olu.Log.Error().Err(err).Msg("can't get image blob manifest") + + return "", "" + } + + imageConfig, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest) + if err != nil { + olu.Log.Error().Err(err).Msg("extension api: error reading image config") + + return "", "" + } + + return imageConfig.OS, imageConfig.Architecture +} + +func (olu BaseOciLayoutUtils) GetImageVendor(repo string, manifestDigest godigest.Digest) string { + imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest) + if err != nil { + olu.Log.Error().Err(err).Msg("can't get image blob manifest") + + return "" + } + + imageConfig, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest) + if err != nil { + olu.Log.Error().Err(err).Msg("extension api: error reading image config") + + return "" + } + + return imageConfig.Config.Labels["vendor"] +} + +func (olu BaseOciLayoutUtils) GetImageManifestSize(repo string, manifestDigest godigest.Digest) int64 { + imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest) + if err != nil { + olu.Log.Error().Err(err).Msg("can't get image blob manifest") + + return 0 + } + + return imageBlobManifest.Config.Size +} + +func (olu BaseOciLayoutUtils) GetImageConfigSize(repo string, manifestDigest godigest.Digest) int64 { + imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest) + if err != nil { + olu.Log.Error().Err(err).Msg("can't get image blob manifest") + + return 0 + } + imageStore := olu.StoreController.GetImageStore(repo) + + buf, err := imageStore.GetBlobContent(repo, imageBlobManifest.Config.Digest.String()) + if err != nil { + olu.Log.Error().Err(err).Msg("error when getting blob content") + + return int64(len(buf)) + } + + return int64(len(buf)) +} + +func (olu BaseOciLayoutUtils) GetRepoLastUpdated(repo string) (time.Time, error) { + tagsInfo, err := olu.GetImageTagsWithTimestamp(repo) + if err != nil || len(tagsInfo) == 0 { + return time.Time{}, err + } + + latestTag := GetLatestTag(tagsInfo) + + return latestTag.Timestamp, nil +} + +func (olu BaseOciLayoutUtils) GetExpandedRepoInfo(name string) (RepoInfo, error) { repo := RepoInfo{} manifests := make([]Manifest, 0) diff --git a/pkg/extensions/search/cve/cve.go b/pkg/extensions/search/cve/cve.go index 5f3ae962..c91b80f0 100644 --- a/pkg/extensions/search/cve/cve.go +++ b/pkg/extensions/search/cve/cve.go @@ -78,7 +78,7 @@ func ScanImage(ctx *cli.Context) (report.Report, error) { func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) { cveController := CveTrivyController{} - layoutUtils := common.NewOciLayoutUtils(storeController, log) + layoutUtils := common.NewBaseOciLayoutUtils(storeController, log) subCveConfig := make(map[string]*TrivyCtx) diff --git a/pkg/extensions/search/cve/cve_test.go b/pkg/extensions/search/cve/cve_test.go index 63ba1fed..0ece828b 100644 --- a/pkg/extensions/search/cve/cve_test.go +++ b/pkg/extensions/search/cve/cve_test.go @@ -94,7 +94,7 @@ func testSetup() error { storeController := storage.StoreController{DefaultStore: storage.NewImageStore(dir, false, storage.DefaultGCDelay, false, false, log, metrics)} - layoutUtils := common.NewOciLayoutUtils(storeController, log) + layoutUtils := common.NewBaseOciLayoutUtils(storeController, log) cve = &cveinfo.CveInfo{Log: log, StoreController: storeController, LayoutUtils: layoutUtils} diff --git a/pkg/extensions/search/cve/models.go b/pkg/extensions/search/cve/models.go index 47c7c330..c4e24b94 100644 --- a/pkg/extensions/search/cve/models.go +++ b/pkg/extensions/search/cve/models.go @@ -13,7 +13,7 @@ type CveInfo struct { Log log.Logger CveTrivyController CveTrivyController StoreController storage.StoreController - LayoutUtils *common.OciLayoutUtils + LayoutUtils *common.BaseOciLayoutUtils } type CveTrivyController struct { diff --git a/pkg/extensions/search/digest/digest.go b/pkg/extensions/search/digest/digest.go index 9925a324..34043247 100644 --- a/pkg/extensions/search/digest/digest.go +++ b/pkg/extensions/search/digest/digest.go @@ -12,12 +12,12 @@ import ( // DigestInfo implements searching by manifes/config/layer digest. type DigestInfo struct { Log log.Logger - LayoutUtils *common.OciLayoutUtils + LayoutUtils *common.BaseOciLayoutUtils } // NewDigestInfo initializes a new DigestInfo object. func NewDigestInfo(storeController storage.StoreController, log log.Logger) *DigestInfo { - layoutUtils := common.NewOciLayoutUtils(storeController, log) + layoutUtils := common.NewBaseOciLayoutUtils(storeController, log) return &DigestInfo{Log: log, LayoutUtils: layoutUtils} } diff --git a/pkg/extensions/search/gql_generated/generated.go b/pkg/extensions/search/gql_generated/generated.go index d24fee9c..0261ee6f 100644 --- a/pkg/extensions/search/gql_generated/generated.go +++ b/pkg/extensions/search/gql_generated/generated.go @@ -55,6 +55,12 @@ type ComplexityRoot struct { Tag func(childComplexity int) int } + GlobalSearchResult struct { + Images func(childComplexity int) int + Layers func(childComplexity int) int + Repos func(childComplexity int) int + } + ImageInfo struct { Description func(childComplexity int) int Labels func(childComplexity int) int @@ -66,6 +72,17 @@ type ComplexityRoot struct { Vendor func(childComplexity int) int } + ImageSummary struct { + IsSigned func(childComplexity int) int + LastUpdated func(childComplexity int) int + Platform func(childComplexity int) int + RepoName func(childComplexity int) int + Score func(childComplexity int) int + Size func(childComplexity int) int + Tag func(childComplexity int) int + Vendor func(childComplexity int) int + } + ImgResultForCVE struct { Name func(childComplexity int) int Tags func(childComplexity int) int @@ -85,6 +102,12 @@ type ComplexityRoot struct { Size func(childComplexity int) int } + LayerSummary struct { + Digest func(childComplexity int) int + Score func(childComplexity int) int + Size func(childComplexity int) int + } + ManifestInfo struct { Digest func(childComplexity int) int IsSigned func(childComplexity int) int @@ -92,6 +115,11 @@ type ComplexityRoot struct { Tag func(childComplexity int) int } + OsArch struct { + Arch func(childComplexity int) int + Os func(childComplexity int) int + } + PackageInfo struct { FixedVersion func(childComplexity int) int InstalledVersion func(childComplexity int) int @@ -101,6 +129,7 @@ type ComplexityRoot struct { Query struct { CVEListForImage func(childComplexity int, image string) int ExpandedRepoInfo func(childComplexity int, repo string) int + GlobalSearch func(childComplexity int, query string) int ImageListForCve func(childComplexity int, id string) int ImageListForDigest func(childComplexity int, id string) int ImageListWithCVEFixed func(childComplexity int, id string, image string) int @@ -111,6 +140,15 @@ type ComplexityRoot struct { Manifests func(childComplexity int) int } + RepoSummary struct { + LastUpdated func(childComplexity int) int + Name func(childComplexity int) int + Platforms func(childComplexity int) int + Score func(childComplexity int) int + Size func(childComplexity int) int + Vendors func(childComplexity int) int + } + TagInfo struct { Digest func(childComplexity int) int Name func(childComplexity int) int @@ -125,6 +163,7 @@ type QueryResolver interface { ImageListForDigest(ctx context.Context, id string) ([]*ImgResultForDigest, error) ImageListWithLatestTag(ctx context.Context) ([]*ImageInfo, error) ExpandedRepoInfo(ctx context.Context, repo string) (*RepoInfo, error) + GlobalSearch(ctx context.Context, query string) (*GlobalSearchResult, error) } type executableSchema struct { @@ -191,6 +230,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CVEResultForImage.Tag(childComplexity), true + case "GlobalSearchResult.Images": + if e.complexity.GlobalSearchResult.Images == nil { + break + } + + return e.complexity.GlobalSearchResult.Images(childComplexity), true + + case "GlobalSearchResult.Layers": + if e.complexity.GlobalSearchResult.Layers == nil { + break + } + + return e.complexity.GlobalSearchResult.Layers(childComplexity), true + + case "GlobalSearchResult.Repos": + if e.complexity.GlobalSearchResult.Repos == nil { + break + } + + return e.complexity.GlobalSearchResult.Repos(childComplexity), true + case "ImageInfo.Description": if e.complexity.ImageInfo.Description == nil { break @@ -247,6 +307,62 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ImageInfo.Vendor(childComplexity), true + case "ImageSummary.IsSigned": + if e.complexity.ImageSummary.IsSigned == nil { + break + } + + return e.complexity.ImageSummary.IsSigned(childComplexity), true + + case "ImageSummary.LastUpdated": + if e.complexity.ImageSummary.LastUpdated == nil { + break + } + + return e.complexity.ImageSummary.LastUpdated(childComplexity), true + + case "ImageSummary.Platform": + if e.complexity.ImageSummary.Platform == nil { + break + } + + return e.complexity.ImageSummary.Platform(childComplexity), true + + case "ImageSummary.RepoName": + if e.complexity.ImageSummary.RepoName == nil { + break + } + + return e.complexity.ImageSummary.RepoName(childComplexity), true + + case "ImageSummary.Score": + if e.complexity.ImageSummary.Score == nil { + break + } + + return e.complexity.ImageSummary.Score(childComplexity), true + + case "ImageSummary.Size": + if e.complexity.ImageSummary.Size == nil { + break + } + + return e.complexity.ImageSummary.Size(childComplexity), true + + case "ImageSummary.Tag": + if e.complexity.ImageSummary.Tag == nil { + break + } + + return e.complexity.ImageSummary.Tag(childComplexity), true + + case "ImageSummary.Vendor": + if e.complexity.ImageSummary.Vendor == nil { + break + } + + return e.complexity.ImageSummary.Vendor(childComplexity), true + case "ImgResultForCVE.Name": if e.complexity.ImgResultForCVE.Name == nil { break @@ -296,6 +412,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.LayerInfo.Size(childComplexity), true + case "LayerSummary.Digest": + if e.complexity.LayerSummary.Digest == nil { + break + } + + return e.complexity.LayerSummary.Digest(childComplexity), true + + case "LayerSummary.Score": + if e.complexity.LayerSummary.Score == nil { + break + } + + return e.complexity.LayerSummary.Score(childComplexity), true + + case "LayerSummary.Size": + if e.complexity.LayerSummary.Size == nil { + break + } + + return e.complexity.LayerSummary.Size(childComplexity), true + case "ManifestInfo.Digest": if e.complexity.ManifestInfo.Digest == nil { break @@ -324,6 +461,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ManifestInfo.Tag(childComplexity), true + case "OsArch.Arch": + if e.complexity.OsArch.Arch == nil { + break + } + + return e.complexity.OsArch.Arch(childComplexity), true + + case "OsArch.Os": + if e.complexity.OsArch.Os == nil { + break + } + + return e.complexity.OsArch.Os(childComplexity), true + case "PackageInfo.FixedVersion": if e.complexity.PackageInfo.FixedVersion == nil { break @@ -369,6 +520,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.ExpandedRepoInfo(childComplexity, args["repo"].(string)), true + case "Query.GlobalSearch": + if e.complexity.Query.GlobalSearch == nil { + break + } + + args, err := ec.field_Query_GlobalSearch_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.GlobalSearch(childComplexity, args["query"].(string)), true + case "Query.ImageListForCVE": if e.complexity.Query.ImageListForCve == nil { break @@ -419,6 +582,48 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.RepoInfo.Manifests(childComplexity), true + case "RepoSummary.LastUpdated": + if e.complexity.RepoSummary.LastUpdated == nil { + break + } + + return e.complexity.RepoSummary.LastUpdated(childComplexity), true + + case "RepoSummary.Name": + if e.complexity.RepoSummary.Name == nil { + break + } + + return e.complexity.RepoSummary.Name(childComplexity), true + + case "RepoSummary.Platforms": + if e.complexity.RepoSummary.Platforms == nil { + break + } + + return e.complexity.RepoSummary.Platforms(childComplexity), true + + case "RepoSummary.Score": + if e.complexity.RepoSummary.Score == nil { + break + } + + return e.complexity.RepoSummary.Score(childComplexity), true + + case "RepoSummary.Size": + if e.complexity.RepoSummary.Size == nil { + break + } + + return e.complexity.RepoSummary.Size(childComplexity), true + + case "RepoSummary.Vendors": + if e.complexity.RepoSummary.Vendors == nil { + break + } + + return e.complexity.RepoSummary.Vendors(childComplexity), true + case "TagInfo.Digest": if e.complexity.TagInfo.Digest == nil { break @@ -560,6 +765,50 @@ type LayerInfo { Digest: String } +# Search results in all repos/images/layers +# There will be other more structures for more detailed information +type GlobalSearchResult { + Images: [ImageSummary] + Repos: [RepoSummary] + Layers: [LayerSummary] +} + +# Brief on a specific image to be used in queries returning a list of images +# We define an image as a pairing or a repo and a tag belonging to that repo +type ImageSummary { + RepoName: String + Tag: String + LastUpdated: Time + IsSigned: Boolean + Size: String + Platform: OsArch + Vendor: String + Score: Int +} + +# Brief on a specific repo to be used in queries returning a list of repos +type RepoSummary { + Name: String + LastUpdated: Time + Size: String + Platforms: [OsArch] + Vendors: [String] + Score: Int +} + +# Currently the same as LayerInfo, we can refactor later +# For detailed information on the layer a ImageListForDigest call can be made +type LayerSummary { + Size: String # Int64 is not supported. + Digest: String + Score: Int +} + +type OsArch { + Os: String + Arch: String +} + type Query { CVEListForImage(image: String!) :CVEResultForImage ImageListForCVE(id: String!) :[ImgResultForCVE] @@ -567,6 +816,7 @@ type Query { ImageListForDigest(id: String!) :[ImgResultForDigest] ImageListWithLatestTag:[ImageInfo] ExpandedRepoInfo(repo: String!):RepoInfo + GlobalSearch(query: String!): GlobalSearchResult } `, BuiltIn: false}, } @@ -606,6 +856,21 @@ func (ec *executionContext) field_Query_ExpandedRepoInfo_args(ctx context.Contex return args, nil } +func (ec *executionContext) field_Query_GlobalSearch_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["query"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("query")) + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["query"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_ImageListForCVE_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -1020,6 +1285,169 @@ func (ec *executionContext) fieldContext_CVEResultForImage_CVEList(ctx context.C return fc, nil } +func (ec *executionContext) _GlobalSearchResult_Images(ctx context.Context, field graphql.CollectedField, obj *GlobalSearchResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobalSearchResult_Images(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Images, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*ImageSummary) + fc.Result = res + return ec.marshalOImageSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobalSearchResult_Images(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobalSearchResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "RepoName": + return ec.fieldContext_ImageSummary_RepoName(ctx, field) + case "Tag": + return ec.fieldContext_ImageSummary_Tag(ctx, field) + case "LastUpdated": + return ec.fieldContext_ImageSummary_LastUpdated(ctx, field) + case "IsSigned": + return ec.fieldContext_ImageSummary_IsSigned(ctx, field) + case "Size": + return ec.fieldContext_ImageSummary_Size(ctx, field) + case "Platform": + return ec.fieldContext_ImageSummary_Platform(ctx, field) + case "Vendor": + return ec.fieldContext_ImageSummary_Vendor(ctx, field) + case "Score": + return ec.fieldContext_ImageSummary_Score(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ImageSummary", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobalSearchResult_Repos(ctx context.Context, field graphql.CollectedField, obj *GlobalSearchResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobalSearchResult_Repos(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Repos, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*RepoSummary) + fc.Result = res + return ec.marshalORepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobalSearchResult_Repos(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobalSearchResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Name": + return ec.fieldContext_RepoSummary_Name(ctx, field) + case "LastUpdated": + return ec.fieldContext_RepoSummary_LastUpdated(ctx, field) + case "Size": + return ec.fieldContext_RepoSummary_Size(ctx, field) + case "Platforms": + return ec.fieldContext_RepoSummary_Platforms(ctx, field) + case "Vendors": + return ec.fieldContext_RepoSummary_Vendors(ctx, field) + case "Score": + return ec.fieldContext_RepoSummary_Score(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type RepoSummary", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _GlobalSearchResult_Layers(ctx context.Context, field graphql.CollectedField, obj *GlobalSearchResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_GlobalSearchResult_Layers(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Layers, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*LayerSummary) + fc.Result = res + return ec.marshalOLayerSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐLayerSummary(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_GlobalSearchResult_Layers(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "GlobalSearchResult", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Size": + return ec.fieldContext_LayerSummary_Size(ctx, field) + case "Digest": + return ec.fieldContext_LayerSummary_Digest(ctx, field) + case "Score": + return ec.fieldContext_LayerSummary_Score(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type LayerSummary", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _ImageInfo_Name(ctx context.Context, field graphql.CollectedField, obj *ImageInfo) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ImageInfo_Name(ctx, field) if err != nil { @@ -1348,6 +1776,340 @@ func (ec *executionContext) fieldContext_ImageInfo_Labels(ctx context.Context, f return fc, nil } +func (ec *executionContext) _ImageSummary_RepoName(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_RepoName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.RepoName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_RepoName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_Tag(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_Tag(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Tag, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_Tag(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_LastUpdated(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_LastUpdated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.LastUpdated, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*time.Time) + fc.Result = res + return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_LastUpdated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_IsSigned(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_IsSigned(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsSigned, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*bool) + fc.Result = res + return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_IsSigned(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_Size(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_Size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_Size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_Platform(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_Platform(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Platform, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*OsArch) + fc.Result = res + return ec.marshalOOsArch2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐOsArch(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_Platform(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Os": + return ec.fieldContext_OsArch_Os(ctx, field) + case "Arch": + return ec.fieldContext_OsArch_Arch(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OsArch", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_Vendor(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_Vendor(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Vendor, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_Vendor(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _ImageSummary_Score(ctx context.Context, field graphql.CollectedField, obj *ImageSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_ImageSummary_Score(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Score, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_ImageSummary_Score(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ImageSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ImgResultForCVE_Name(ctx context.Context, field graphql.CollectedField, obj *ImgResultForCve) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ImgResultForCVE_Name(ctx, field) if err != nil { @@ -1643,6 +2405,129 @@ func (ec *executionContext) fieldContext_LayerInfo_Digest(ctx context.Context, f return fc, nil } +func (ec *executionContext) _LayerSummary_Size(ctx context.Context, field graphql.CollectedField, obj *LayerSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LayerSummary_Size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LayerSummary_Size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LayerSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LayerSummary_Digest(ctx context.Context, field graphql.CollectedField, obj *LayerSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LayerSummary_Digest(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Digest, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LayerSummary_Digest(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LayerSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LayerSummary_Score(ctx context.Context, field graphql.CollectedField, obj *LayerSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_LayerSummary_Score(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Score, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_LayerSummary_Score(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LayerSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _ManifestInfo_Digest(ctx context.Context, field graphql.CollectedField, obj *ManifestInfo) (ret graphql.Marshaler) { fc, err := ec.fieldContext_ManifestInfo_Digest(ctx, field) if err != nil { @@ -1813,6 +2698,88 @@ func (ec *executionContext) fieldContext_ManifestInfo_Layers(ctx context.Context return fc, nil } +func (ec *executionContext) _OsArch_Os(ctx context.Context, field graphql.CollectedField, obj *OsArch) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OsArch_Os(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Os, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OsArch_Os(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OsArch", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _OsArch_Arch(ctx context.Context, field graphql.CollectedField, obj *OsArch) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_OsArch_Arch(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Arch, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_OsArch_Arch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "OsArch", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _PackageInfo_Name(ctx context.Context, field graphql.CollectedField, obj *PackageInfo) (ret graphql.Marshaler) { fc, err := ec.fieldContext_PackageInfo_Name(ctx, field) if err != nil { @@ -2281,6 +3248,66 @@ func (ec *executionContext) fieldContext_Query_ExpandedRepoInfo(ctx context.Cont return fc, nil } +func (ec *executionContext) _Query_GlobalSearch(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_GlobalSearch(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GlobalSearch(rctx, fc.Args["query"].(string)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*GlobalSearchResult) + fc.Result = res + return ec.marshalOGlobalSearchResult2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐGlobalSearchResult(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_GlobalSearch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Images": + return ec.fieldContext_GlobalSearchResult_Images(ctx, field) + case "Repos": + return ec.fieldContext_GlobalSearchResult_Repos(ctx, field) + case "Layers": + return ec.fieldContext_GlobalSearchResult_Layers(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type GlobalSearchResult", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_GlobalSearch_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -2461,6 +3488,258 @@ func (ec *executionContext) fieldContext_RepoInfo_Manifests(ctx context.Context, return fc, nil } +func (ec *executionContext) _RepoSummary_Name(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_Name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_Name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _RepoSummary_LastUpdated(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_LastUpdated(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.LastUpdated, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*time.Time) + fc.Result = res + return ec.marshalOTime2ᚖtimeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_LastUpdated(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _RepoSummary_Size(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_Size(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Size, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_Size(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _RepoSummary_Platforms(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_Platforms(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Platforms, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*OsArch) + fc.Result = res + return ec.marshalOOsArch2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐOsArch(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_Platforms(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "Os": + return ec.fieldContext_OsArch_Os(ctx, field) + case "Arch": + return ec.fieldContext_OsArch_Arch(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type OsArch", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _RepoSummary_Vendors(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_Vendors(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Vendors, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*string) + fc.Result = res + return ec.marshalOString2ᚕᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_Vendors(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _RepoSummary_Score(ctx context.Context, field graphql.CollectedField, obj *RepoSummary) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_RepoSummary_Score(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Score, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*int) + fc.Result = res + return ec.marshalOInt2ᚖint(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_RepoSummary_Score(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "RepoSummary", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _TagInfo_Name(ctx context.Context, field graphql.CollectedField, obj *TagInfo) (ret graphql.Marshaler) { fc, err := ec.fieldContext_TagInfo_Name(ctx, field) if err != nil { @@ -4435,6 +5714,39 @@ func (ec *executionContext) _CVEResultForImage(ctx context.Context, sel ast.Sele return out } +var globalSearchResultImplementors = []string{"GlobalSearchResult"} + +func (ec *executionContext) _GlobalSearchResult(ctx context.Context, sel ast.SelectionSet, obj *GlobalSearchResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, globalSearchResultImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("GlobalSearchResult") + case "Images": + + out.Values[i] = ec._GlobalSearchResult_Images(ctx, field, obj) + + case "Repos": + + out.Values[i] = ec._GlobalSearchResult_Repos(ctx, field, obj) + + case "Layers": + + out.Values[i] = ec._GlobalSearchResult_Layers(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var imageInfoImplementors = []string{"ImageInfo"} func (ec *executionContext) _ImageInfo(ctx context.Context, sel ast.SelectionSet, obj *ImageInfo) graphql.Marshaler { @@ -4488,6 +5800,59 @@ func (ec *executionContext) _ImageInfo(ctx context.Context, sel ast.SelectionSet return out } +var imageSummaryImplementors = []string{"ImageSummary"} + +func (ec *executionContext) _ImageSummary(ctx context.Context, sel ast.SelectionSet, obj *ImageSummary) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, imageSummaryImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("ImageSummary") + case "RepoName": + + out.Values[i] = ec._ImageSummary_RepoName(ctx, field, obj) + + case "Tag": + + out.Values[i] = ec._ImageSummary_Tag(ctx, field, obj) + + case "LastUpdated": + + out.Values[i] = ec._ImageSummary_LastUpdated(ctx, field, obj) + + case "IsSigned": + + out.Values[i] = ec._ImageSummary_IsSigned(ctx, field, obj) + + case "Size": + + out.Values[i] = ec._ImageSummary_Size(ctx, field, obj) + + case "Platform": + + out.Values[i] = ec._ImageSummary_Platform(ctx, field, obj) + + case "Vendor": + + out.Values[i] = ec._ImageSummary_Vendor(ctx, field, obj) + + case "Score": + + out.Values[i] = ec._ImageSummary_Score(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var imgResultForCVEImplementors = []string{"ImgResultForCVE"} func (ec *executionContext) _ImgResultForCVE(ctx context.Context, sel ast.SelectionSet, obj *ImgResultForCve) graphql.Marshaler { @@ -4600,6 +5965,39 @@ func (ec *executionContext) _LayerInfo(ctx context.Context, sel ast.SelectionSet return out } +var layerSummaryImplementors = []string{"LayerSummary"} + +func (ec *executionContext) _LayerSummary(ctx context.Context, sel ast.SelectionSet, obj *LayerSummary) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, layerSummaryImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("LayerSummary") + case "Size": + + out.Values[i] = ec._LayerSummary_Size(ctx, field, obj) + + case "Digest": + + out.Values[i] = ec._LayerSummary_Digest(ctx, field, obj) + + case "Score": + + out.Values[i] = ec._LayerSummary_Score(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var manifestInfoImplementors = []string{"ManifestInfo"} func (ec *executionContext) _ManifestInfo(ctx context.Context, sel ast.SelectionSet, obj *ManifestInfo) graphql.Marshaler { @@ -4637,6 +6035,35 @@ func (ec *executionContext) _ManifestInfo(ctx context.Context, sel ast.Selection return out } +var osArchImplementors = []string{"OsArch"} + +func (ec *executionContext) _OsArch(ctx context.Context, sel ast.SelectionSet, obj *OsArch) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, osArchImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("OsArch") + case "Os": + + out.Values[i] = ec._OsArch_Os(ctx, field, obj) + + case "Arch": + + out.Values[i] = ec._OsArch_Arch(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var packageInfoImplementors = []string{"PackageInfo"} func (ec *executionContext) _PackageInfo(ctx context.Context, sel ast.SelectionSet, obj *PackageInfo) graphql.Marshaler { @@ -4806,6 +6233,26 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) } + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "GlobalSearch": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_GlobalSearch(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) @@ -4857,6 +6304,51 @@ func (ec *executionContext) _RepoInfo(ctx context.Context, sel ast.SelectionSet, return out } +var repoSummaryImplementors = []string{"RepoSummary"} + +func (ec *executionContext) _RepoSummary(ctx context.Context, sel ast.SelectionSet, obj *RepoSummary) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, repoSummaryImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("RepoSummary") + case "Name": + + out.Values[i] = ec._RepoSummary_Name(ctx, field, obj) + + case "LastUpdated": + + out.Values[i] = ec._RepoSummary_LastUpdated(ctx, field, obj) + + case "Size": + + out.Values[i] = ec._RepoSummary_Size(ctx, field, obj) + + case "Platforms": + + out.Values[i] = ec._RepoSummary_Platforms(ctx, field, obj) + + case "Vendors": + + out.Values[i] = ec._RepoSummary_Vendors(ctx, field, obj) + + case "Score": + + out.Values[i] = ec._RepoSummary_Score(ctx, field, obj) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var tagInfoImplementors = []string{"TagInfo"} func (ec *executionContext) _TagInfo(ctx context.Context, sel ast.SelectionSet, obj *TagInfo) graphql.Marshaler { @@ -5572,6 +7064,13 @@ func (ec *executionContext) marshalOCVEResultForImage2ᚖzotregistryᚗioᚋzot return ec._CVEResultForImage(ctx, sel, v) } +func (ec *executionContext) marshalOGlobalSearchResult2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐGlobalSearchResult(ctx context.Context, sel ast.SelectionSet, v *GlobalSearchResult) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._GlobalSearchResult(ctx, sel, v) +} + func (ec *executionContext) marshalOImageInfo2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageInfo(ctx context.Context, sel ast.SelectionSet, v []*ImageInfo) graphql.Marshaler { if v == nil { return graphql.Null @@ -5620,6 +7119,54 @@ func (ec *executionContext) marshalOImageInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋ return ec._ImageInfo(ctx, sel, v) } +func (ec *executionContext) marshalOImageSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx context.Context, sel ast.SelectionSet, v []*ImageSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOImageSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalOImageSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImageSummary(ctx context.Context, sel ast.SelectionSet, v *ImageSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._ImageSummary(ctx, sel, v) +} + func (ec *executionContext) marshalOImgResultForCVE2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐImgResultForCve(ctx context.Context, sel ast.SelectionSet, v []*ImgResultForCve) graphql.Marshaler { if v == nil { return graphql.Null @@ -5723,6 +7270,22 @@ func (ec *executionContext) marshalOImgResultForFixedCVE2ᚖzotregistryᚗioᚋz return ec._ImgResultForFixedCVE(ctx, sel, v) } +func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v interface{}) (*int, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalInt(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOInt2ᚖint(ctx context.Context, sel ast.SelectionSet, v *int) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalInt(*v) + return res +} + func (ec *executionContext) marshalOLayerInfo2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐLayerInfo(ctx context.Context, sel ast.SelectionSet, v []*LayerInfo) graphql.Marshaler { if v == nil { return graphql.Null @@ -5771,6 +7334,54 @@ func (ec *executionContext) marshalOLayerInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋ return ec._LayerInfo(ctx, sel, v) } +func (ec *executionContext) marshalOLayerSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐLayerSummary(ctx context.Context, sel ast.SelectionSet, v []*LayerSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOLayerSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐLayerSummary(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalOLayerSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐLayerSummary(ctx context.Context, sel ast.SelectionSet, v *LayerSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._LayerSummary(ctx, sel, v) +} + func (ec *executionContext) marshalOManifestInfo2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐManifestInfo(ctx context.Context, sel ast.SelectionSet, v []*ManifestInfo) graphql.Marshaler { if v == nil { return graphql.Null @@ -5819,6 +7430,54 @@ func (ec *executionContext) marshalOManifestInfo2ᚖzotregistryᚗioᚋzotᚋpkg return ec._ManifestInfo(ctx, sel, v) } +func (ec *executionContext) marshalOOsArch2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐOsArch(ctx context.Context, sel ast.SelectionSet, v []*OsArch) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalOOsArch2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐOsArch(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalOOsArch2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐOsArch(ctx context.Context, sel ast.SelectionSet, v *OsArch) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._OsArch(ctx, sel, v) +} + func (ec *executionContext) marshalOPackageInfo2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐPackageInfo(ctx context.Context, sel ast.SelectionSet, v []*PackageInfo) graphql.Marshaler { if v == nil { return graphql.Null @@ -5874,6 +7533,54 @@ func (ec *executionContext) marshalORepoInfo2ᚖzotregistryᚗioᚋzotᚋpkgᚋe return ec._RepoInfo(ctx, sel, v) } +func (ec *executionContext) marshalORepoSummary2ᚕᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx context.Context, sel ast.SelectionSet, v []*RepoSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalORepoSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + return ret +} + +func (ec *executionContext) marshalORepoSummary2ᚖzotregistryᚗioᚋzotᚋpkgᚋextensionsᚋsearchᚋgql_generatedᚐRepoSummary(ctx context.Context, sel ast.SelectionSet, v *RepoSummary) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._RepoSummary(ctx, sel, v) +} + func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) { if v == nil { return nil, nil diff --git a/pkg/extensions/search/gql_generated/models_gen.go b/pkg/extensions/search/gql_generated/models_gen.go index 0fa49337..fc637222 100644 --- a/pkg/extensions/search/gql_generated/models_gen.go +++ b/pkg/extensions/search/gql_generated/models_gen.go @@ -19,6 +19,12 @@ type CVEResultForImage struct { CVEList []*Cve `json:"CVEList"` } +type GlobalSearchResult struct { + Images []*ImageSummary `json:"Images"` + Repos []*RepoSummary `json:"Repos"` + Layers []*LayerSummary `json:"Layers"` +} + type ImageInfo struct { Name *string `json:"Name"` Latest *string `json:"Latest"` @@ -30,6 +36,17 @@ type ImageInfo struct { Labels *string `json:"Labels"` } +type ImageSummary struct { + RepoName *string `json:"RepoName"` + Tag *string `json:"Tag"` + LastUpdated *time.Time `json:"LastUpdated"` + IsSigned *bool `json:"IsSigned"` + Size *string `json:"Size"` + Platform *OsArch `json:"Platform"` + Vendor *string `json:"Vendor"` + Score *int `json:"Score"` +} + type ImgResultForCve struct { Name *string `json:"Name"` Tags []*string `json:"Tags"` @@ -49,6 +66,12 @@ type LayerInfo struct { Digest *string `json:"Digest"` } +type LayerSummary struct { + Size *string `json:"Size"` + Digest *string `json:"Digest"` + Score *int `json:"Score"` +} + type ManifestInfo struct { Digest *string `json:"Digest"` Tag *string `json:"Tag"` @@ -56,6 +79,11 @@ type ManifestInfo struct { Layers []*LayerInfo `json:"Layers"` } +type OsArch struct { + Os *string `json:"Os"` + Arch *string `json:"Arch"` +} + type PackageInfo struct { Name *string `json:"Name"` InstalledVersion *string `json:"InstalledVersion"` @@ -66,6 +94,15 @@ type RepoInfo struct { Manifests []*ManifestInfo `json:"Manifests"` } +type RepoSummary struct { + Name *string `json:"Name"` + LastUpdated *time.Time `json:"LastUpdated"` + Size *string `json:"Size"` + Platforms []*OsArch `json:"Platforms"` + Vendors []*string `json:"Vendors"` + Score *int `json:"Score"` +} + type TagInfo struct { Name *string `json:"Name"` Digest *string `json:"Digest"` diff --git a/pkg/extensions/search/resolver.go b/pkg/extensions/search/resolver.go index df898af6..496e9ee4 100644 --- a/pkg/extensions/search/resolver.go +++ b/pkg/extensions/search/resolver.go @@ -5,7 +5,9 @@ package search // It serves as dependency injection for your app, add any dependencies you require here. import ( + "sort" "strconv" + "strings" godigest "github.com/opencontainers/go-digest" "zotregistry.io/zot/pkg/log" // nolint: gci @@ -123,7 +125,7 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]* r.log.Info().Msg("no repositories found") } - layoutUtils := common.NewOciLayoutUtils(r.storeController, r.log) + layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log) for _, repo := range repoList { tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo) @@ -186,6 +188,180 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]* return results, nil } +func cleanQuerry(query string) string { + query = strings.ToLower(query) + query = strings.Replace(query, ":", " ", 1) + + return query +} + +func globalSearch(repoList []string, name, tag string, olu common.OciLayoutUtils, log log.Logger) ( + []*gql_generated.RepoSummary, []*gql_generated.ImageSummary, []*gql_generated.LayerSummary, +) { + repos := []*gql_generated.RepoSummary{} + images := []*gql_generated.ImageSummary{} + layers := []*gql_generated.LayerSummary{} + + for _, repo := range repoList { + repo := repo + + // map used for dedube if 2 images reference the same blob + repoLayerBlob2Size := make(map[string]int64, 10) + + // made up of all manifests, configs and image layers + repoSize := int64(0) + + lastUpdate, err := olu.GetRepoLastUpdated(repo) + if err != nil { + log.Error().Err(err).Msgf("can't find latest update timestamp for repo: %s", repo) + } + + tagsInfo, err := olu.GetImageTagsWithTimestamp(repo) + if err != nil { + log.Error().Err(err).Msgf("can't get tags info for repo: %s", repo) + + continue + } + + repoInfo, err := olu.GetExpandedRepoInfo(repo) + if err != nil { + log.Error().Err(err).Msgf("can't get repo info for repo: %s", repo) + + continue + } + + repoPlatforms := make([]*gql_generated.OsArch, 0, len(tagsInfo)) + repoVendors := make([]*string, 0, len(repoInfo.Manifests)) + + for i, manifest := range repoInfo.Manifests { + imageLayersSize := int64(0) + manifestSize := olu.GetImageManifestSize(repo, godigest.Digest(tagsInfo[i].Digest)) + configSize := olu.GetImageConfigSize(repo, godigest.Digest(tagsInfo[i].Digest)) + + for _, layer := range manifest.Layers { + layer := layer + + layerSize, err := strconv.ParseInt(layer.Size, 10, 64) + if err != nil { + log.Error().Err(err).Msg("invalid layer size") + + continue + } + + repoLayerBlob2Size[layer.Digest] = layerSize + imageLayersSize += layerSize + + // if we have a tag we won't match a layer + if tag != "" { + continue + } + + if index := strings.Index(layer.Digest, name); index != -1 { + layers = append(layers, &gql_generated.LayerSummary{ + Digest: &layer.Digest, + Size: &layer.Size, + Score: &index, + }) + } + } + + imageSize := imageLayersSize + manifestSize + configSize + repoSize += manifestSize + configSize + + index := strings.Index(repo, name) + matchesTag := strings.HasPrefix(manifest.Tag, tag) + + if index != -1 { + tag := manifest.Tag + size := strconv.Itoa(int(imageSize)) + vendor := olu.GetImageVendor(repo, godigest.Digest(tagsInfo[i].Digest)) + lastUpdated := olu.GetImageLastUpdated(repo, godigest.Digest(tagsInfo[i].Digest)) + + isSigned := manifest.IsSigned + // update matching score + score := calculateImageMatchingScore(repo, index, matchesTag) + + os, arch := olu.GetImagePlatform(repo, godigest.Digest(tagsInfo[i].Digest)) + osArch := &gql_generated.OsArch{ + Os: &os, + Arch: &arch, + } + + repoPlatforms = append(repoPlatforms, osArch) + repoVendors = append(repoVendors, &vendor) + + images = append(images, &gql_generated.ImageSummary{ + RepoName: &repo, + Tag: &tag, + LastUpdated: &lastUpdated, + IsSigned: &isSigned, + Size: &size, + Platform: osArch, + Vendor: &vendor, + Score: &score, + }) + } + } + + for layerBlob := range repoLayerBlob2Size { + repoSize += repoLayerBlob2Size[layerBlob] + } + + if index := strings.Index(repo, name); index != -1 { + repoSize := strconv.FormatInt(repoSize, 10) + + repos = append(repos, &gql_generated.RepoSummary{ + Name: &repo, + LastUpdated: &lastUpdate, + Size: &repoSize, + Platforms: repoPlatforms, + Vendors: repoVendors, + Score: &index, + }) + } + } + + sort.Slice(repos, func(i, j int) bool { + return *repos[i].Score < *repos[j].Score + }) + + sort.Slice(images, func(i, j int) bool { + return *images[i].Score < *images[j].Score + }) + + sort.Slice(layers, func(i, j int) bool { + return *layers[i].Score < *layers[j].Score + }) + + return repos, images, layers +} + +// calcalculateImageMatchingScore iterated from the index of the matched string in the +// artifact name until the beginning of the string or until delimitator "/". +// The distance represents the score of the match. +// +// Example: +// query: image +// repos: repo/test/myimage +// Score will be 2. +func calculateImageMatchingScore(artefactName string, index int, matchesTag bool) int { + score := 0 + + for index >= 1 { + if artefactName[index-1] == '/' { + break + } + index-- + score++ + } + + if !matchesTag { + score += 10 + } + + return score +} + func getGraphqlCompatibleTags(fixedTags []common.TagInfo) []*gql_generated.TagInfo { finalTagList := make([]*gql_generated.TagInfo, 0) diff --git a/pkg/extensions/search/resolver_test.go b/pkg/extensions/search/resolver_test.go new file mode 100644 index 00000000..cc5dd6fa --- /dev/null +++ b/pkg/extensions/search/resolver_test.go @@ -0,0 +1,154 @@ +package search //nolint + +import ( + "errors" + "strings" + "testing" + "time" + + godigest "github.com/opencontainers/go-digest" + . "github.com/smartystreets/goconvey/convey" + "zotregistry.io/zot/pkg/extensions/search/common" + "zotregistry.io/zot/pkg/log" + "zotregistry.io/zot/pkg/test/mocks" +) + +var ErrTestError = errors.New("TestError") + +func TestGlobalSearch(t *testing.T) { + Convey("globalSearch", t, func() { + Convey("GetRepoLastUpdated fail", func() { + mockOlum := mocks.OciLayoutUtilsMock{ + GetRepoLastUpdatedFn: func(repo string) (time.Time, error) { + return time.Time{}, ErrTestError + }, + } + + globalSearch([]string{"repo1"}, "name", "tag", mockOlum, log.NewLogger("debug", "")) + }) + + Convey("GetImageTagsWithTimestamp fail", func() { + mockOlum := mocks.OciLayoutUtilsMock{ + GetImageTagsWithTimestampFn: func(repo string) ([]common.TagInfo, error) { + return []common.TagInfo{}, ErrTestError + }, + } + + globalSearch([]string{"repo1"}, "name", "tag", mockOlum, log.NewLogger("debug", "")) + }) + + Convey("GetExpandedRepoInfo fail", func() { + mockOlum := mocks.OciLayoutUtilsMock{ + GetExpandedRepoInfoFn: func(name string) (common.RepoInfo, error) { + return common.RepoInfo{}, ErrTestError + }, + } + + globalSearch([]string{"repo1"}, "name", "tag", mockOlum, log.NewLogger("debug", "")) + }) + + Convey("Bad layer digest in manifest", func() { + mockOlum := mocks.OciLayoutUtilsMock{ + GetExpandedRepoInfoFn: func(name string) (common.RepoInfo, error) { + return common.RepoInfo{ + Manifests: []common.Manifest{ + { + Tag: "latest", + Layers: []common.Layer{ + { + Size: "this is a bad size format", + Digest: "digest", + }, + }, + }, + }, + }, nil + }, + GetImageManifestSizeFn: func(repo string, manifestDigest godigest.Digest) int64 { + return 100 + }, + GetImageConfigSizeFn: func(repo string, manifestDigest godigest.Digest) int64 { + return 100 + }, + GetImageTagsWithTimestampFn: func(repo string) ([]common.TagInfo, error) { + return []common.TagInfo{ + { + Name: "test", + Digest: "test", + }, + }, nil + }, + } + globalSearch([]string{"repo1"}, "name", "tag", mockOlum, log.NewLogger("debug", "")) + }) + + Convey("Tag given, no layer match", func() { + mockOlum := mocks.OciLayoutUtilsMock{ + GetExpandedRepoInfoFn: func(name string) (common.RepoInfo, error) { + return common.RepoInfo{ + Manifests: []common.Manifest{ + { + Tag: "latest", + Layers: []common.Layer{ + { + Size: "100", + Digest: "sha256:855b1556a45637abf05c63407437f6f305b4627c4361fb965a78e5731999c0c7", + }, + }, + }, + }, + }, nil + }, + GetImageManifestSizeFn: func(repo string, manifestDigest godigest.Digest) int64 { + return 100 + }, + GetImageConfigSizeFn: func(repo string, manifestDigest godigest.Digest) int64 { + return 100 + }, + GetImageTagsWithTimestampFn: func(repo string) ([]common.TagInfo, error) { + return []common.TagInfo{ + { + Name: "test", + Digest: "test", + }, + }, nil + }, + } + globalSearch([]string{"repo1"}, "name", "tag", mockOlum, log.NewLogger("debug", "")) + }) + }) +} + +func TestMatching(t *testing.T) { + pine := "pine" + + Convey("Perfect Matching", t, func() { + query := "alpine" + score := calculateImageMatchingScore("alpine", strings.Index("alpine", query), true) + So(score, ShouldEqual, 0) + }) + + Convey("Partial Matching", t, func() { + query := pine + score := calculateImageMatchingScore("alpine", strings.Index("alpine", query), true) + So(score, ShouldEqual, 2) + }) + + Convey("Complex Partial Matching", t, func() { + query := pine + score := calculateImageMatchingScore("repo/test/alpine", strings.Index("alpine", query), true) + So(score, ShouldEqual, 2) + + query = pine + score = calculateImageMatchingScore("repo/alpine/test", strings.Index("alpine", query), true) + So(score, ShouldEqual, 2) + + query = pine + score = calculateImageMatchingScore("alpine/repo/test", strings.Index("alpine", query), true) + So(score, ShouldEqual, 2) + + query = pine + score = calculateImageMatchingScore("alpine/repo/test", strings.Index("alpine", query), false) + So(score, ShouldEqual, 12) + }) +} diff --git a/pkg/extensions/search/schema.graphql b/pkg/extensions/search/schema.graphql index 7b10d794..9db10c4b 100644 --- a/pkg/extensions/search/schema.graphql +++ b/pkg/extensions/search/schema.graphql @@ -66,6 +66,50 @@ type LayerInfo { Digest: String } +# Search results in all repos/images/layers +# There will be other more structures for more detailed information +type GlobalSearchResult { + Images: [ImageSummary] + Repos: [RepoSummary] + Layers: [LayerSummary] +} + +# Brief on a specific image to be used in queries returning a list of images +# We define an image as a pairing or a repo and a tag belonging to that repo +type ImageSummary { + RepoName: String + Tag: String + LastUpdated: Time + IsSigned: Boolean + Size: String + Platform: OsArch + Vendor: String + Score: Int +} + +# Brief on a specific repo to be used in queries returning a list of repos +type RepoSummary { + Name: String + LastUpdated: Time + Size: String + Platforms: [OsArch] + Vendors: [String] + Score: Int +} + +# Currently the same as LayerInfo, we can refactor later +# For detailed information on the layer a ImageListForDigest call can be made +type LayerSummary { + Size: String # Int64 is not supported. + Digest: String + Score: Int +} + +type OsArch { + Os: String + Arch: String +} + type Query { CVEListForImage(image: String!) :CVEResultForImage ImageListForCVE(id: String!) :[ImgResultForCVE] @@ -73,4 +117,5 @@ type Query { ImageListForDigest(id: String!) :[ImgResultForDigest] ImageListWithLatestTag:[ImageInfo] ExpandedRepoInfo(repo: String!):RepoInfo + GlobalSearch(query: String!): GlobalSearchResult } diff --git a/pkg/extensions/search/schema.resolvers.go b/pkg/extensions/search/schema.resolvers.go index d07e8a33..aa299b0b 100644 --- a/pkg/extensions/search/schema.resolvers.go +++ b/pkg/extensions/search/schema.resolvers.go @@ -319,7 +319,7 @@ func (r *queryResolver) ImageListWithLatestTag(ctx context.Context) ([]*gql_gene // ExpandedRepoInfo is the resolver for the ExpandedRepoInfo field. func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql_generated.RepoInfo, error) { - olu := common.NewOciLayoutUtils(r.storeController, r.log) + olu := common.NewBaseOciLayoutUtils(r.storeController, r.log) origRepoInfo, err := olu.GetExpandedRepoInfo(repo) if err != nil { @@ -364,6 +364,35 @@ func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql return repoInfo, nil } +// GlobalSearch is the resolver for the GlobalSearch field. +func (r *queryResolver) GlobalSearch(ctx context.Context, query string) (*gql_generated.GlobalSearchResult, error) { + query = cleanQuerry(query) + defaultStore := r.storeController.DefaultStore + olu := common.NewBaseOciLayoutUtils(r.storeController, r.log) + + var name, tag string + + _, err := fmt.Sscanf(query, "%s %s", &name, &tag) + if err != nil { + name = query + } + + repoList, err := defaultStore.GetRepositories() + if err != nil { + r.log.Error().Err(err).Msg("unable to search repositories") + + return &gql_generated.GlobalSearchResult{}, err + } + + repos, images, layers := globalSearch(repoList, name, tag, olu, r.log) + + return &gql_generated.GlobalSearchResult{ + Images: images, + Repos: repos, + Layers: layers, + }, nil +} + // Query returns gql_generated.QueryResolver implementation. func (r *Resolver) Query() gql_generated.QueryResolver { return &queryResolver{r} } diff --git a/pkg/storage/s3/s3_test.go b/pkg/storage/s3/s3_test.go index 43e41dca..a68e3d8b 100644 --- a/pkg/storage/s3/s3_test.go +++ b/pkg/storage/s3/s3_test.go @@ -101,8 +101,8 @@ func createObjectsStore(rootDir string, cacheDir string, dedupe bool) ( } type FileInfoMock struct { - isDirFn func() bool - sizeFn func() int64 + IsDirFn func() bool + SizeFn func() int64 } func (f *FileInfoMock) Path() string { @@ -110,8 +110,8 @@ func (f *FileInfoMock) Path() string { } func (f *FileInfoMock) Size() int64 { - if f != nil && f.sizeFn != nil { - return f.sizeFn() + if f != nil && f.SizeFn != nil { + return f.SizeFn() } return int64(fileInfoSize) @@ -122,18 +122,18 @@ func (f *FileInfoMock) ModTime() time.Time { } func (f *FileInfoMock) IsDir() bool { - if f != nil && f.isDirFn != nil { - return f.isDirFn() + if f != nil && f.IsDirFn != nil { + return f.IsDirFn() } return true } type FileWriterMock struct { - writeFn func([]byte) (int, error) - cancelFn func() error - commitFn func() error - closeFn func() error + WriteFn func([]byte) (int, error) + CancelFn func() error + CommitFn func() error + CloseFn func() error } func (f *FileWriterMock) Size() int64 { @@ -141,117 +141,117 @@ func (f *FileWriterMock) Size() int64 { } func (f *FileWriterMock) Cancel() error { - if f != nil && f.cancelFn != nil { - return f.cancelFn() + if f != nil && f.CancelFn != nil { + return f.CancelFn() } return nil } func (f *FileWriterMock) Commit() error { - if f != nil && f.commitFn != nil { - return f.commitFn() + if f != nil && f.CommitFn != nil { + return f.CommitFn() } return nil } func (f *FileWriterMock) Write(p []byte) (int, error) { - if f != nil && f.writeFn != nil { - return f.writeFn(p) + if f != nil && f.WriteFn != nil { + return f.WriteFn(p) } return 10, nil } func (f *FileWriterMock) Close() error { - if f != nil && f.closeFn != nil { - return f.closeFn() + if f != nil && f.CloseFn != nil { + return f.CloseFn() } return nil } type StorageDriverMock struct { - nameFn func() string - getContentFn func(ctx context.Context, path string) ([]byte, error) - putContentFn func(ctx context.Context, path string, content []byte) error - readerFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) - writerFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) - statFn func(ctx context.Context, path string) (driver.FileInfo, error) - listFn func(ctx context.Context, path string) ([]string, error) - moveFn func(ctx context.Context, sourcePath, destPath string) error - deleteFn func(ctx context.Context, path string) error - walkFn func(ctx context.Context, path string, f driver.WalkFn) error + NameFn func() string + GetContentFn func(ctx context.Context, path string) ([]byte, error) + PutContentFn func(ctx context.Context, path string, content []byte) error + ReaderFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) + WriterFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) + StatFn func(ctx context.Context, path string) (driver.FileInfo, error) + ListFn func(ctx context.Context, path string) ([]string, error) + MoveFn func(ctx context.Context, sourcePath, destPath string) error + DeleteFn func(ctx context.Context, path string) error + WalkFn func(ctx context.Context, path string, f driver.WalkFn) error } func (s *StorageDriverMock) Name() string { - if s != nil && s.nameFn != nil { - return s.nameFn() + if s != nil && s.NameFn != nil { + return s.NameFn() } return "" } func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) { - if s != nil && s.getContentFn != nil { - return s.getContentFn(ctx, path) + if s != nil && s.GetContentFn != nil { + return s.GetContentFn(ctx, path) } return []byte{}, nil } func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error { - if s != nil && s.putContentFn != nil { - return s.putContentFn(ctx, path, content) + if s != nil && s.PutContentFn != nil { + return s.PutContentFn(ctx, path, content) } return nil } func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { - if s != nil && s.readerFn != nil { - return s.readerFn(ctx, path, offset) + if s != nil && s.ReaderFn != nil { + return s.ReaderFn(ctx, path, offset) } return ioutil.NopCloser(strings.NewReader("")), nil } func (s *StorageDriverMock) Writer(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { - if s != nil && s.writerFn != nil { - return s.writerFn(ctx, path, isAppend) + if s != nil && s.WriterFn != nil { + return s.WriterFn(ctx, path, isAppend) } return &FileWriterMock{}, nil } func (s *StorageDriverMock) Stat(ctx context.Context, path string) (driver.FileInfo, error) { - if s != nil && s.statFn != nil { - return s.statFn(ctx, path) + if s != nil && s.StatFn != nil { + return s.StatFn(ctx, path) } return &FileInfoMock{}, nil } func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) { - if s != nil && s.listFn != nil { - return s.listFn(ctx, path) + if s != nil && s.ListFn != nil { + return s.ListFn(ctx, path) } return []string{"a"}, nil } func (s *StorageDriverMock) Move(ctx context.Context, sourcePath, destPath string) error { - if s != nil && s.moveFn != nil { - return s.moveFn(ctx, sourcePath, destPath) + if s != nil && s.MoveFn != nil { + return s.MoveFn(ctx, sourcePath, destPath) } return nil } func (s *StorageDriverMock) Delete(ctx context.Context, path string) error { - if s != nil && s.deleteFn != nil { - return s.deleteFn(ctx, path) + if s != nil && s.DeleteFn != nil { + return s.DeleteFn(ctx, path) } return nil @@ -262,8 +262,8 @@ func (s *StorageDriverMock) URLFor(ctx context.Context, path string, options map } func (s *StorageDriverMock) Walk(ctx context.Context, path string, f driver.WalkFn) error { - if s != nil && s.walkFn != nil { - return s.walkFn(ctx, path, f) + if s != nil && s.WalkFn != nil { + return s.WalkFn(ctx, path, f) } return nil @@ -458,31 +458,31 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test storage driver errors", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{testImage}, errS3 }, - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { return errS3 }, - getContentFn: func(ctx context.Context, path string) ([]byte, error) { + GetContentFn: func(ctx context.Context, path string) ([]byte, error) { return []byte{}, errS3 }, - putContentFn: func(ctx context.Context, path string, content []byte) error { + PutContentFn: func(ctx context.Context, path string, content []byte) error { return errS3 }, - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{}, errS3 }, - readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { + ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { return ioutil.NopCloser(strings.NewReader("")), errS3 }, - walkFn: func(ctx context.Context, path string, f driver.WalkFn) error { + WalkFn: func(ctx context.Context, path string, f driver.WalkFn) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, errS3 }, - deleteFn: func(ctx context.Context, path string) error { + DeleteFn: func(ctx context.Context, path string) error { return errS3 }, }) @@ -532,7 +532,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{testImage, testImage}, errS3 }, }) @@ -541,10 +541,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { So(err, ShouldNotBeNil) imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{testImage, testImage}, nil }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return nil, errS3 }, }) @@ -555,10 +555,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test ValidateRepo2", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{"test/test/oci-layout", "test/test/index.json"}, nil }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, nil }, }) @@ -568,13 +568,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test ValidateRepo3", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{"test/test/oci-layout", "test/test/index.json"}, nil }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, nil }, - getContentFn: func(ctx context.Context, path string) ([]byte, error) { + GetContentFn: func(ctx context.Context, path string) ([]byte, error) { return []byte{}, errS3 }, }) @@ -585,13 +585,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test ValidateRepo4", t, func(c C) { ociLayout := []byte(`{"imageLayoutVersion": "9.9.9"}`) imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - listFn: func(ctx context.Context, path string) ([]string, error) { + ListFn: func(ctx context.Context, path string) ([]string, error) { return []string{"test/test/oci-layout", "test/test/index.json"}, nil }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, nil }, - getContentFn: func(ctx context.Context, path string) ([]byte, error) { + GetContentFn: func(ctx context.Context, path string) ([]byte, error) { return ociLayout, nil }, }) @@ -601,7 +601,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test GetRepositories", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - walkFn: func(ctx context.Context, path string, f driver.WalkFn) error { + WalkFn: func(ctx context.Context, path string, f driver.WalkFn) error { return f(new(FileInfoMock)) }, }) @@ -612,7 +612,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test DeleteImageManifest", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - getContentFn: func(ctx context.Context, path string) ([]byte, error) { + GetContentFn: func(ctx context.Context, path string) ([]byte, error) { return []byte{}, errS3 }, }) @@ -628,7 +628,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test NewBlobUpload", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - putContentFn: func(ctx context.Context, path string, content []byte) error { + PutContentFn: func(ctx context.Context, path string, content []byte) error { return errS3 }, }) @@ -638,7 +638,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test GetBlobUpload", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, errS3 }, }) @@ -648,7 +648,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test PutBlobChunkStreamed", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{}, errS3 }, }) @@ -658,8 +658,8 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test PutBlobChunkStreamed2", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { - return &FileWriterMock{writeFn: func(b []byte) (int, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + return &FileWriterMock{WriteFn: func(b []byte) (int, error) { return 0, errS3 }}, nil }, @@ -670,7 +670,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test PutBlobChunk", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{}, errS3 }, }) @@ -680,12 +680,12 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test PutBlobChunk2", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{ - writeFn: func(b []byte) (int, error) { + WriteFn: func(b []byte) (int, error) { return 0, errS3 }, - cancelFn: func() error { + CancelFn: func() error { return errS3 }, }, nil @@ -697,9 +697,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test PutBlobChunk3", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{ - writeFn: func(b []byte) (int, error) { + WriteFn: func(b []byte) (int, error) { return 0, errS3 }, }, nil @@ -711,9 +711,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FinishBlobUpload", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{ - commitFn: func() error { + CommitFn: func() error { return errS3 }, }, nil @@ -726,9 +726,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FinishBlobUpload2", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{ - closeFn: func() error { + CloseFn: func() error { return errS3 }, }, nil @@ -741,7 +741,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FinishBlobUpload3", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { + ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { return nil, errS3 }, }) @@ -752,7 +752,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FinishBlobUpload4", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { return errS3 }, }) @@ -763,7 +763,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FullBlobUpload", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{}, errS3 }, }) @@ -781,7 +781,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test FullBlobUpload3", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { return errS3 }, }) @@ -792,7 +792,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test GetBlob", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { + ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { return ioutil.NopCloser(strings.NewReader("")), errS3 }, }) @@ -803,7 +803,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test DeleteBlob", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - deleteFn: func(ctx context.Context, path string) error { + DeleteFn: func(ctx context.Context, path string) error { return errS3 }, }) @@ -814,7 +814,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) { Convey("Test GetReferrers", t, func(c C) { imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ - deleteFn: func(ctx context.Context, path string) error { + DeleteFn: func(ctx context.Context, path string) error { return errS3 }, }) @@ -1128,10 +1128,10 @@ func TestS3DedupeErr(t *testing.T) { So(err, ShouldNotBeNil) imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath string, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath string, destPath string) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return driver.FileInfoInternal{}, errS3 }, }) @@ -1148,7 +1148,7 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test DedupeBlob - error on second store.Stat()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { if path == "dst2" { return driver.FileInfoInternal{}, errS3 } @@ -1168,10 +1168,10 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test DedupeBlob - error on store.PutContent()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - putContentFn: func(ctx context.Context, path string, content []byte) error { + PutContentFn: func(ctx context.Context, path string, content []byte) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return nil, nil }, }) @@ -1187,10 +1187,10 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test DedupeBlob - error on store.Delete()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - deleteFn: func(ctx context.Context, path string) error { + DeleteFn: func(ctx context.Context, path string) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return nil, nil }, }) @@ -1206,13 +1206,13 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test copyBlob() - error on initRepo()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - putContentFn: func(ctx context.Context, path string, content []byte) error { + PutContentFn: func(ctx context.Context, path string, content []byte) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return driver.FileInfoInternal{}, errS3 }, - writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { return &FileWriterMock{}, errS3 }, }) @@ -1230,10 +1230,10 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test copyBlob() - error on store.PutContent()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - putContentFn: func(ctx context.Context, path string, content []byte) error { + PutContentFn: func(ctx context.Context, path string, content []byte) error { return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return driver.FileInfoInternal{}, errS3 }, }) @@ -1251,7 +1251,7 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test copyBlob() - error on store.Stat()", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return driver.FileInfoInternal{}, errS3 }, }) @@ -1290,7 +1290,7 @@ func TestS3DedupeErr(t *testing.T) { So(err, ShouldBeNil) imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { if strings.Contains(path, "repo1/dst1") { return driver.FileInfoInternal{}, driver.PathNotFoundError{} } @@ -1327,14 +1327,14 @@ func TestS3DedupeErr(t *testing.T) { So(err, ShouldBeNil) imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{ - sizeFn: func() int64 { + SizeFn: func() int64 { return 0 }, }, nil }, - readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { + ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { if strings.Contains(path, "repo1/dst1") { return ioutil.NopCloser(strings.NewReader("")), errS3 } @@ -1356,14 +1356,14 @@ func TestS3DedupeErr(t *testing.T) { blobPath := path.Join(testDir, "repo/blobs/sha256", hash) imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { if destPath == blobPath { return nil } return errS3 }, - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { if path != blobPath { return nil, errS3 } @@ -1385,7 +1385,7 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test FullBlobUpload", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { return errS3 }, }) @@ -1397,7 +1397,7 @@ func TestS3DedupeErr(t *testing.T) { Convey("Test FinishBlobUpload", t, func(c C) { tdir := t.TempDir() imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ - moveFn: func(ctx context.Context, sourcePath, destPath string) error { + MoveFn: func(ctx context.Context, sourcePath, destPath string) error { return errS3 }, }) @@ -1419,7 +1419,7 @@ func TestInjectDedupe(t *testing.T) { Convey("Inject errors in DedupeBlob function", t, func() { imgStore := createMockStorage(testDir, tdir, true, &StorageDriverMock{ - statFn: func(ctx context.Context, path string) (driver.FileInfo, error) { + StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) { return &FileInfoMock{}, errS3 }, }) diff --git a/pkg/test/mocks/image_store_mock.go b/pkg/test/mocks/image_store_mock.go new file mode 100644 index 00000000..a8ec0c6c --- /dev/null +++ b/pkg/test/mocks/image_store_mock.go @@ -0,0 +1,289 @@ +package mocks + +import ( + "io" + "time" + + "github.com/opencontainers/go-digest" + artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1" +) + +type MockedImageStore struct { + DirExistsFn func(d string) bool + RootDirFn func() string + InitRepoFn func(name string) error + ValidateRepoFn func(name string) (bool, error) + GetRepositoriesFn func() ([]string, error) + GetImageTagsFn func(repo string) ([]string, error) + GetImageManifestFn func(repo string, reference string) ([]byte, string, string, error) + PutImageManifestFn func(repo string, reference string, mediaType string, body []byte) (string, error) + DeleteImageManifestFn func(repo string, reference string) error + BlobUploadPathFn func(repo string, uuid string) string + NewBlobUploadFn func(repo string) (string, error) + GetBlobUploadFn func(repo string, uuid string) (int64, error) + BlobUploadInfoFn func(repo string, uuid string) (int64, error) + PutBlobChunkStreamedFn func(repo string, uuid string, body io.Reader) (int64, error) + PutBlobChunkFn func(repo string, uuid string, from int64, to int64, body io.Reader) (int64, error) + FinishBlobUploadFn func(repo string, uuid string, body io.Reader, digest string) error + FullBlobUploadFn func(repo string, body io.Reader, digest string) (string, int64, error) + DedupeBlobFn func(src string, dstDigest digest.Digest, dst string) error + DeleteBlobUploadFn func(repo string, uuid string) error + BlobPathFn func(repo string, digest digest.Digest) string + CheckBlobFn func(repo string, digest string) (bool, int64, error) + GetBlobFn func(repo string, digest string, mediaType string) (io.Reader, int64, error) + DeleteBlobFn func(repo string, digest string) error + GetIndexContentFn func(repo string) ([]byte, error) + GetBlobContentFn func(repo, digest string) ([]byte, error) + GetReferrersFn func(repo, digest string, mediaType string) ([]artifactspec.Descriptor, error) + URLForPathFn func(path string) (string, error) + RunGCRepoFn func(repo string) +} + +func (is MockedImageStore) Lock(t *time.Time) { +} + +func (is MockedImageStore) Unlock(t *time.Time) { +} + +func (is MockedImageStore) RUnlock(t *time.Time) { +} + +func (is MockedImageStore) RLock(t *time.Time) { +} + +func (is MockedImageStore) DirExists(d string) bool { + if is.DirExistsFn != nil { + return is.DirExistsFn(d) + } + + return true +} + +func (is MockedImageStore) RootDir() string { + if is.RootDirFn != nil { + return is.RootDirFn() + } + + return "" +} + +func (is MockedImageStore) InitRepo(name string) error { + if is.InitRepoFn != nil { + return is.InitRepoFn(name) + } + + return nil +} + +func (is MockedImageStore) ValidateRepo(name string) (bool, error) { + if is.ValidateRepoFn != nil { + return is.ValidateRepoFn(name) + } + + return true, nil +} + +func (is MockedImageStore) GetRepositories() ([]string, error) { + if is.GetRepositoriesFn != nil { + return is.GetRepositoriesFn() + } + + return []string{}, nil +} + +func (is MockedImageStore) GetImageManifest(repo string, reference string) ([]byte, string, string, error) { + if is.GetImageManifestFn != nil { + return is.GetImageManifestFn(repo, reference) + } + + return []byte{}, "", "", nil +} + +func (is MockedImageStore) PutImageManifest( + repo string, + reference string, + mediaType string, + body []byte, +) (string, error) { + if is.PutImageManifestFn != nil { + return is.PutImageManifestFn(repo, reference, mediaType, body) + } + + return "", nil +} + +func (is MockedImageStore) GetImageTags(name string) ([]string, error) { + if is.GetImageTagsFn != nil { + return is.GetImageTagsFn(name) + } + + return []string{}, nil +} + +func (is MockedImageStore) DeleteImageManifest(name string, reference string) error { + if is.DeleteImageManifestFn != nil { + return is.DeleteImageManifestFn(name, reference) + } + + return nil +} + +func (is MockedImageStore) NewBlobUpload(repo string) (string, error) { + if is.NewBlobUploadFn != nil { + return is.NewBlobUploadFn(repo) + } + + return "", nil +} + +func (is MockedImageStore) GetBlobUpload(repo string, uuid string) (int64, error) { + if is.GetBlobUploadFn != nil { + return is.GetBlobUploadFn(repo, uuid) + } + + return 0, nil +} + +func (is MockedImageStore) BlobUploadInfo(repo string, uuid string) (int64, error) { + if is.BlobUploadInfoFn != nil { + return is.BlobUploadInfoFn(repo, uuid) + } + + return 0, nil +} + +func (is MockedImageStore) BlobUploadPath(repo string, uuid string) string { + if is.BlobUploadPathFn != nil { + return is.BlobUploadPathFn(repo, uuid) + } + + return "" +} + +func (is MockedImageStore) PutBlobChunkStreamed(repo string, uuid string, body io.Reader) (int64, error) { + if is.PutBlobChunkStreamedFn != nil { + return is.PutBlobChunkStreamedFn(repo, uuid, body) + } + + return 0, nil +} + +func (is MockedImageStore) PutBlobChunk( + repo string, + uuid string, + from int64, + to int64, + body io.Reader, +) (int64, error) { + if is.PutBlobChunkFn != nil { + return is.PutBlobChunkFn(repo, uuid, from, to, body) + } + + return 0, nil +} + +func (is MockedImageStore) FinishBlobUpload(repo string, uuid string, body io.Reader, digest string) error { + if is.FinishBlobUploadFn != nil { + return is.FinishBlobUploadFn(repo, uuid, body, digest) + } + + return nil +} + +func (is MockedImageStore) FullBlobUpload(repo string, body io.Reader, digest string) (string, int64, error) { + if is.FullBlobUploadFn != nil { + return is.FullBlobUploadFn(repo, body, digest) + } + + return "", 0, nil +} + +func (is MockedImageStore) DedupeBlob(src string, dstDigest digest.Digest, dst string) error { + if is.DedupeBlobFn != nil { + return is.DedupeBlobFn(src, dstDigest, dst) + } + + return nil +} + +func (is MockedImageStore) DeleteBlob(repo string, digest string) error { + if is.DeleteBlobFn != nil { + return is.DeleteBlobFn(repo, digest) + } + + return nil +} + +func (is MockedImageStore) BlobPath(repo string, digest digest.Digest) string { + if is.BlobPathFn != nil { + return is.BlobPathFn(repo, digest) + } + + return "" +} + +func (is MockedImageStore) CheckBlob(repo string, digest string) (bool, int64, error) { + if is.CheckBlobFn != nil { + return is.CheckBlobFn(repo, digest) + } + + return true, 0, nil +} + +func (is MockedImageStore) GetBlob(repo string, digest string, mediaType string) (io.Reader, int64, error) { + if is.GetBlobFn != nil { + return is.GetBlobFn(repo, digest, mediaType) + } + + return &io.LimitedReader{}, 0, nil +} + +func (is MockedImageStore) DeleteBlobUpload(repo string, digest string) error { + if is.DeleteBlobUploadFn != nil { + return is.DeleteBlobUploadFn(repo, digest) + } + + return nil +} + +func (is MockedImageStore) GetIndexContent(repo string) ([]byte, error) { + if is.GetIndexContentFn != nil { + return is.GetIndexContentFn(repo) + } + + return []byte{}, nil +} + +func (is MockedImageStore) GetBlobContent(repo string, digest string) ([]byte, error) { + if is.GetBlobContentFn != nil { + return is.GetBlobContentFn(repo, digest) + } + + return []byte{}, nil +} + +func (is MockedImageStore) GetReferrers( + repo string, + digest string, + mediaType string, +) ([]artifactspec.Descriptor, error) { + if is.GetReferrersFn != nil { + return is.GetReferrersFn(repo, digest, mediaType) + } + + return []artifactspec.Descriptor{}, nil +} + +func (is MockedImageStore) URLForPath(path string) (string, error) { + if is.URLForPathFn != nil { + return is.URLForPathFn(path) + } + + return "", nil +} + +func (is MockedImageStore) RunGCRepo(repo string) { + if is.RunGCRepoFn != nil { + is.RunGCRepoFn(repo) + } +} diff --git a/pkg/test/mocks/oci_mock.go b/pkg/test/mocks/oci_mock.go new file mode 100644 index 00000000..cf7fe463 --- /dev/null +++ b/pkg/test/mocks/oci_mock.go @@ -0,0 +1,121 @@ +package mocks + +import ( + "time" + + v1 "github.com/google/go-containerregistry/pkg/v1" + godigest "github.com/opencontainers/go-digest" + ispec "github.com/opencontainers/image-spec/specs-go/v1" + "zotregistry.io/zot/pkg/extensions/search/common" +) + +type OciLayoutUtilsMock struct { + GetImageManifestsFn func(image string) ([]ispec.Descriptor, error) + GetImageBlobManifestFn func(imageDir string, digest godigest.Digest) (v1.Manifest, error) + GetImageInfoFn func(imageDir string, hash v1.Hash) (ispec.Image, error) + IsValidImageFormatFn func(image string) (bool, error) + GetImageTagsWithTimestampFn func(repo string) ([]common.TagInfo, error) + GetImageLastUpdatedFn func(repo string, manifestDigest godigest.Digest) time.Time + GetImagePlatformFn func(repo string, manifestDigest godigest.Digest) (string, string) + GetImageVendorFn func(repo string, manifestDigest godigest.Digest) string + GetImageManifestSizeFn func(repo string, manifestDigest godigest.Digest) int64 + GetImageConfigSizeFn func(repo string, manifestDigest godigest.Digest) int64 + GetRepoLastUpdatedFn func(repo string) (time.Time, error) + GetExpandedRepoInfoFn func(name string) (common.RepoInfo, error) +} + +func (olum OciLayoutUtilsMock) GetImageManifests(image string) ([]ispec.Descriptor, error) { + if olum.GetImageBlobManifestFn != nil { + return olum.GetImageManifestsFn(image) + } + + return []ispec.Descriptor{}, nil +} + +func (olum OciLayoutUtilsMock) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) { + if olum.GetImageBlobManifestFn != nil { + return olum.GetImageBlobManifestFn(imageDir, digest) + } + + return v1.Manifest{}, nil +} + +func (olum OciLayoutUtilsMock) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) { + if olum.GetImageInfoFn != nil { + return olum.GetImageInfoFn(imageDir, hash) + } + + return ispec.Image{}, nil +} + +func (olum OciLayoutUtilsMock) IsValidImageFormat(image string) (bool, error) { + if olum.IsValidImageFormatFn != nil { + return olum.IsValidImageFormatFn(image) + } + + return true, nil +} + +func (olum OciLayoutUtilsMock) GetImageTagsWithTimestamp(repo string) ([]common.TagInfo, error) { + if olum.GetImageTagsWithTimestampFn != nil { + return olum.GetImageTagsWithTimestampFn(repo) + } + + return []common.TagInfo{}, nil +} + +func (olum OciLayoutUtilsMock) GetImageLastUpdated(repo string, manifestDigest godigest.Digest) time.Time { + if olum.GetImageLastUpdatedFn != nil { + return olum.GetImageLastUpdatedFn(repo, manifestDigest) + } + + return time.Time{} +} + +func (olum OciLayoutUtilsMock) GetImagePlatform(repo string, manifestDigest godigest.Digest) (string, string) { + if olum.GetImagePlatformFn != nil { + return olum.GetImagePlatformFn(repo, manifestDigest) + } + + return "", "" +} + +func (olum OciLayoutUtilsMock) GetImageVendor(repo string, manifestDigest godigest.Digest) string { + if olum.GetImageVendorFn != nil { + return olum.GetImageVendorFn(repo, manifestDigest) + } + + return "" +} + +func (olum OciLayoutUtilsMock) GetImageManifestSize(repo string, manifestDigest godigest.Digest) int64 { + if olum.GetImageManifestSizeFn != nil { + return olum.GetImageManifestSizeFn(repo, manifestDigest) + } + + return 0 +} + +func (olum OciLayoutUtilsMock) GetImageConfigSize(repo string, manifestDigest godigest.Digest) int64 { + if olum.GetImageConfigSizeFn != nil { + return olum.GetImageConfigSizeFn(repo, manifestDigest) + } + + return 0 +} + +func (olum OciLayoutUtilsMock) GetRepoLastUpdated(repo string) (time.Time, error) { + if olum.GetRepoLastUpdatedFn != nil { + return olum.GetRepoLastUpdatedFn(repo) + } + + return time.Time{}, nil +} + +func (olum OciLayoutUtilsMock) GetExpandedRepoInfo(name string) (common.RepoInfo, error) { + if olum.GetExpandedRepoInfoFn != nil { + return olum.GetExpandedRepoInfoFn(name) + } + + return common.RepoInfo{}, nil +} diff --git a/pkg/test/mocks/storage_driver_mock.go b/pkg/test/mocks/storage_driver_mock.go new file mode 100644 index 00000000..945dd6c6 --- /dev/null +++ b/pkg/test/mocks/storage_driver_mock.go @@ -0,0 +1,186 @@ +package mocks + +import ( + "context" + "io" + "io/ioutil" + "strings" + "time" + + "github.com/docker/distribution/registry/storage/driver" +) + +type StorageDriverMock struct { + NameFn func() string + GetContentFn func(ctx context.Context, path string) ([]byte, error) + PutContentFn func(ctx context.Context, path string, content []byte) error + ReaderFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) + WriterFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) + StatFn func(ctx context.Context, path string) (driver.FileInfo, error) + ListFn func(ctx context.Context, path string) ([]string, error) + MoveFn func(ctx context.Context, sourcePath, destPath string) error + DeleteFn func(ctx context.Context, path string) error + WalkFn func(ctx context.Context, path string, f driver.WalkFn) error +} + +// nolint: gochecknoglobals +var ( + fileWriterSize = 12 + fileInfoSize = 10 +) + +func (s *StorageDriverMock) Name() string { + if s != nil && s.NameFn != nil { + return s.NameFn() + } + + return "" +} + +func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) { + if s != nil && s.GetContentFn != nil { + return s.GetContentFn(ctx, path) + } + + return []byte{}, nil +} + +func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error { + if s != nil && s.PutContentFn != nil { + return s.PutContentFn(ctx, path, content) + } + + return nil +} + +func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { + if s != nil && s.ReaderFn != nil { + return s.ReaderFn(ctx, path, offset) + } + + return ioutil.NopCloser(strings.NewReader("")), nil +} + +func (s *StorageDriverMock) Writer(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { + if s != nil && s.WriterFn != nil { + return s.WriterFn(ctx, path, isAppend) + } + + return &FileWriterMock{}, nil +} + +func (s *StorageDriverMock) Stat(ctx context.Context, path string) (driver.FileInfo, error) { + if s != nil && s.StatFn != nil { + return s.StatFn(ctx, path) + } + + return &FileInfoMock{}, nil +} + +func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) { + if s != nil && s.ListFn != nil { + return s.ListFn(ctx, path) + } + + return []string{"a"}, nil +} + +func (s *StorageDriverMock) Move(ctx context.Context, sourcePath, destPath string) error { + if s != nil && s.MoveFn != nil { + return s.MoveFn(ctx, sourcePath, destPath) + } + + return nil +} + +func (s *StorageDriverMock) Delete(ctx context.Context, path string) error { + if s != nil && s.DeleteFn != nil { + return s.DeleteFn(ctx, path) + } + + return nil +} + +func (s *StorageDriverMock) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) { + return "", nil +} + +func (s *StorageDriverMock) Walk(ctx context.Context, path string, f driver.WalkFn) error { + if s != nil && s.WalkFn != nil { + return s.WalkFn(ctx, path, f) + } + + return nil +} + +type FileInfoMock struct { + IsDirFn func() bool + SizeFn func() int64 +} + +func (f *FileInfoMock) Path() string { + return "" +} + +func (f *FileInfoMock) Size() int64 { + if f != nil && f.SizeFn != nil { + return f.SizeFn() + } + + return int64(fileInfoSize) +} + +func (f *FileInfoMock) ModTime() time.Time { + return time.Now() +} + +func (f *FileInfoMock) IsDir() bool { + if f != nil && f.IsDirFn != nil { + return f.IsDirFn() + } + + return true +} + +type FileWriterMock struct { + WriteFn func([]byte) (int, error) + CancelFn func() error + CommitFn func() error + CloseFn func() error +} + +func (f *FileWriterMock) Size() int64 { + return int64(fileWriterSize) +} + +func (f *FileWriterMock) Cancel() error { + if f != nil && f.CancelFn != nil { + return f.CancelFn() + } + + return nil +} + +func (f *FileWriterMock) Commit() error { + if f != nil && f.CommitFn != nil { + return f.CommitFn() + } + + return nil +} + +func (f *FileWriterMock) Write(p []byte) (int, error) { + if f != nil && f.WriteFn != nil { + return f.WriteFn(p) + } + + return 10, nil +} + +func (f *FileWriterMock) Close() error { + if f != nil && f.CloseFn != nil { + return f.CloseFn() + } + + return nil +}