0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-16 21:56:37 -05:00

freeform querry api

Signed-off-by: Laurentiu Niculae <themelopeus@gmail.com>
This commit is contained in:
Laurentiu Niculae 2022-07-12 15:58:04 +03:00 committed by Ramkumar Chinchani
parent a31869f270
commit 7e3d063319
20 changed files with 3597 additions and 562 deletions

View file

@ -4,3 +4,4 @@ ignore:
- "./pkg/extensions/minimal.go" - "./pkg/extensions/minimal.go"
- "./pkg/cli/minimal.go" - "./pkg/cli/minimal.go"
- "./cmd/zb/*.go" - "./cmd/zb/*.go"
- "./pkg/test/mocks/*.go"

8
go.mod
View file

@ -361,16 +361,16 @@ require (
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.21.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/net v0.0.0-20220524220425-1d687d428aca // indirect
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // 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/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.11 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/api v0.81.0 // indirect google.golang.org/api v0.81.0 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect

14
go.sum
View file

@ -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-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.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-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.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= 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= 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= 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.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.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-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-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/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-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-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-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-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-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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/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.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.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.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.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-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-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-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-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-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-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/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-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=

File diff suppressed because it is too large Load diff

View file

@ -6,8 +6,10 @@ package common_test
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"os/exec" "os/exec"
"path" "path"
@ -30,12 +32,15 @@ import (
"zotregistry.io/zot/pkg/log" "zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/storage" "zotregistry.io/zot/pkg/storage"
. "zotregistry.io/zot/pkg/test" . "zotregistry.io/zot/pkg/test"
"zotregistry.io/zot/pkg/test/mocks"
) )
const ( const (
graphqlQueryPrefix = constants.ExtSearchPrefix graphqlQueryPrefix = constants.ExtSearchPrefix
) )
var ErrTestError = errors.New("test error")
// nolint:gochecknoglobals // nolint:gochecknoglobals
var ( var (
rootDir string rootDir string
@ -52,6 +57,50 @@ type ExpandedRepoInfoResp struct {
Errors []ErrorGQL `json:"errors"` 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 { type ExpandedRepoInfo struct {
RepoInfo common.RepoInfo `json:"expandedRepoInfo"` RepoInfo common.RepoInfo `json:"expandedRepoInfo"`
} }
@ -210,7 +259,7 @@ func TestImageFormat(t *testing.T) {
metrics := monitoring.NewMetricsServer(false, log) metrics := monitoring.NewMetricsServer(false, log)
defaultStore := storage.NewImageStore(dbDir, false, storage.DefaultGCDelay, false, false, log, metrics) defaultStore := storage.NewImageStore(dbDir, false, storage.DefaultGCDelay, false, false, log, metrics)
storeController := storage.StoreController{DefaultStore: defaultStore} storeController := storage.StoreController{DefaultStore: defaultStore}
olu := common.NewOciLayoutUtils(storeController, log) olu := common.NewBaseOciLayoutUtils(storeController, log)
isValidImage, err := olu.IsValidImageFormat("zot-test") isValidImage, err := olu.IsValidImageFormat("zot-test")
So(err, ShouldBeNil) So(err, ShouldBeNil)
@ -671,3 +720,404 @@ func TestUtilsMethod(t *testing.T) {
So(dir, ShouldEqual, subRootDir) 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)
})
}

View file

@ -20,8 +20,23 @@ import (
"zotregistry.io/zot/pkg/storage" "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 ... // OciLayoutInfo ...
type OciLayoutUtils struct { type BaseOciLayoutUtils struct {
Log log.Logger Log log.Logger
StoreController storage.StoreController StoreController storage.StoreController
} }
@ -42,13 +57,13 @@ type Layer struct {
Digest string `json:"digest"` Digest string `json:"digest"`
} }
// NewOciLayoutUtils initializes a new OciLayoutUtils object. // NewBaseOciLayoutUtils initializes a new OciLayoutUtils object.
func NewOciLayoutUtils(storeController storage.StoreController, log log.Logger) *OciLayoutUtils { func NewBaseOciLayoutUtils(storeController storage.StoreController, log log.Logger) *BaseOciLayoutUtils {
return &OciLayoutUtils{Log: log, StoreController: storeController} return &BaseOciLayoutUtils{Log: log, StoreController: storeController}
} }
// Below method will return image path including root dir, root dir is determined by splitting. // 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) imageStore := olu.StoreController.GetImageStore(image)
buf, err := imageStore.GetIndexContent(image) buf, err := imageStore.GetIndexContent(image)
@ -76,7 +91,7 @@ func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, e
} }
//nolint: interfacer //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 var blobIndex v1.Manifest
imageStore := olu.StoreController.GetImageStore(imageDir) imageStore := olu.StoreController.GetImageStore(imageDir)
@ -98,7 +113,7 @@ func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.
} }
//nolint: interfacer //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 var imageInfo ispec.Image
imageStore := olu.StoreController.GetImageStore(imageDir) imageStore := olu.StoreController.GetImageStore(imageDir)
@ -119,7 +134,7 @@ func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Ima
return imageInfo, err return imageInfo, err
} }
func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) { func (olu BaseOciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
imageDir, inputTag := GetImageDirAndTag(image) imageDir, inputTag := GetImageDirAndTag(image)
manifests, err := olu.GetImageManifests(imageDir) 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. // 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) tagsInfo := make([]TagInfo, 0)
manifests, err := olu.GetImageManifests(repo) 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. // 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) imageStore := olu.StoreController.GetImageStore(name)
mediaType := notreg.ArtifactTypeNotation mediaType := notreg.ArtifactTypeNotation
@ -219,7 +234,7 @@ func (olu OciLayoutUtils) checkNotarySignature(name string, digest godigest.Dige
} }
// check cosign signature corresponding to manifest. // 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) imageStore := olu.StoreController.GetImageStore(name)
// if manifest is signed using cosign mechanism, cosign adds a new manifest. // 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 if manifest is signed or not
// checks for notary or cosign signature // checks for notary or cosign signature
// if cosign signature found it does not looks for notary 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) { if !olu.checkCosignSignature(name, digest) {
return olu.checkNotarySignature(name, digest) return olu.checkNotarySignature(name, digest)
} }
@ -248,7 +263,112 @@ func (olu OciLayoutUtils) checkManifestSignature(name string, digest godigest.Di
return true 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{} repo := RepoInfo{}
manifests := make([]Manifest, 0) manifests := make([]Manifest, 0)

View file

@ -78,7 +78,7 @@ func ScanImage(ctx *cli.Context) (report.Report, error) {
func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) { func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) {
cveController := CveTrivyController{} cveController := CveTrivyController{}
layoutUtils := common.NewOciLayoutUtils(storeController, log) layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
subCveConfig := make(map[string]*TrivyCtx) subCveConfig := make(map[string]*TrivyCtx)

View file

@ -94,7 +94,7 @@ func testSetup() error {
storeController := storage.StoreController{DefaultStore: storage.NewImageStore(dir, false, storage.DefaultGCDelay, false, false, log, metrics)} 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} cve = &cveinfo.CveInfo{Log: log, StoreController: storeController, LayoutUtils: layoutUtils}

View file

@ -13,7 +13,7 @@ type CveInfo struct {
Log log.Logger Log log.Logger
CveTrivyController CveTrivyController CveTrivyController CveTrivyController
StoreController storage.StoreController StoreController storage.StoreController
LayoutUtils *common.OciLayoutUtils LayoutUtils *common.BaseOciLayoutUtils
} }
type CveTrivyController struct { type CveTrivyController struct {

View file

@ -12,12 +12,12 @@ import (
// DigestInfo implements searching by manifes/config/layer digest. // DigestInfo implements searching by manifes/config/layer digest.
type DigestInfo struct { type DigestInfo struct {
Log log.Logger Log log.Logger
LayoutUtils *common.OciLayoutUtils LayoutUtils *common.BaseOciLayoutUtils
} }
// NewDigestInfo initializes a new DigestInfo object. // NewDigestInfo initializes a new DigestInfo object.
func NewDigestInfo(storeController storage.StoreController, log log.Logger) *DigestInfo { 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} return &DigestInfo{Log: log, LayoutUtils: layoutUtils}
} }

File diff suppressed because it is too large Load diff

View file

@ -19,6 +19,12 @@ type CVEResultForImage struct {
CVEList []*Cve `json:"CVEList"` CVEList []*Cve `json:"CVEList"`
} }
type GlobalSearchResult struct {
Images []*ImageSummary `json:"Images"`
Repos []*RepoSummary `json:"Repos"`
Layers []*LayerSummary `json:"Layers"`
}
type ImageInfo struct { type ImageInfo struct {
Name *string `json:"Name"` Name *string `json:"Name"`
Latest *string `json:"Latest"` Latest *string `json:"Latest"`
@ -30,6 +36,17 @@ type ImageInfo struct {
Labels *string `json:"Labels"` 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 { type ImgResultForCve struct {
Name *string `json:"Name"` Name *string `json:"Name"`
Tags []*string `json:"Tags"` Tags []*string `json:"Tags"`
@ -49,6 +66,12 @@ type LayerInfo struct {
Digest *string `json:"Digest"` Digest *string `json:"Digest"`
} }
type LayerSummary struct {
Size *string `json:"Size"`
Digest *string `json:"Digest"`
Score *int `json:"Score"`
}
type ManifestInfo struct { type ManifestInfo struct {
Digest *string `json:"Digest"` Digest *string `json:"Digest"`
Tag *string `json:"Tag"` Tag *string `json:"Tag"`
@ -56,6 +79,11 @@ type ManifestInfo struct {
Layers []*LayerInfo `json:"Layers"` Layers []*LayerInfo `json:"Layers"`
} }
type OsArch struct {
Os *string `json:"Os"`
Arch *string `json:"Arch"`
}
type PackageInfo struct { type PackageInfo struct {
Name *string `json:"Name"` Name *string `json:"Name"`
InstalledVersion *string `json:"InstalledVersion"` InstalledVersion *string `json:"InstalledVersion"`
@ -66,6 +94,15 @@ type RepoInfo struct {
Manifests []*ManifestInfo `json:"Manifests"` 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 { type TagInfo struct {
Name *string `json:"Name"` Name *string `json:"Name"`
Digest *string `json:"Digest"` Digest *string `json:"Digest"`

View file

@ -5,7 +5,9 @@ package search
// It serves as dependency injection for your app, add any dependencies you require here. // It serves as dependency injection for your app, add any dependencies you require here.
import ( import (
"sort"
"strconv" "strconv"
"strings"
godigest "github.com/opencontainers/go-digest" godigest "github.com/opencontainers/go-digest"
"zotregistry.io/zot/pkg/log" // nolint: gci "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") 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 { for _, repo := range repoList {
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo) tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
@ -186,6 +188,180 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*
return results, nil 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 { func getGraphqlCompatibleTags(fixedTags []common.TagInfo) []*gql_generated.TagInfo {
finalTagList := make([]*gql_generated.TagInfo, 0) finalTagList := make([]*gql_generated.TagInfo, 0)

View file

@ -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)
})
}

View file

@ -66,6 +66,50 @@ type LayerInfo {
Digest: String 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 { type Query {
CVEListForImage(image: String!) :CVEResultForImage CVEListForImage(image: String!) :CVEResultForImage
ImageListForCVE(id: String!) :[ImgResultForCVE] ImageListForCVE(id: String!) :[ImgResultForCVE]
@ -73,4 +117,5 @@ type Query {
ImageListForDigest(id: String!) :[ImgResultForDigest] ImageListForDigest(id: String!) :[ImgResultForDigest]
ImageListWithLatestTag:[ImageInfo] ImageListWithLatestTag:[ImageInfo]
ExpandedRepoInfo(repo: String!):RepoInfo ExpandedRepoInfo(repo: String!):RepoInfo
GlobalSearch(query: String!): GlobalSearchResult
} }

View file

@ -319,7 +319,7 @@ func (r *queryResolver) ImageListWithLatestTag(ctx context.Context) ([]*gql_gene
// ExpandedRepoInfo is the resolver for the ExpandedRepoInfo field. // ExpandedRepoInfo is the resolver for the ExpandedRepoInfo field.
func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql_generated.RepoInfo, error) { 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) origRepoInfo, err := olu.GetExpandedRepoInfo(repo)
if err != nil { if err != nil {
@ -364,6 +364,35 @@ func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql
return repoInfo, nil 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. // Query returns gql_generated.QueryResolver implementation.
func (r *Resolver) Query() gql_generated.QueryResolver { return &queryResolver{r} } func (r *Resolver) Query() gql_generated.QueryResolver { return &queryResolver{r} }

View file

@ -101,8 +101,8 @@ func createObjectsStore(rootDir string, cacheDir string, dedupe bool) (
} }
type FileInfoMock struct { type FileInfoMock struct {
isDirFn func() bool IsDirFn func() bool
sizeFn func() int64 SizeFn func() int64
} }
func (f *FileInfoMock) Path() string { func (f *FileInfoMock) Path() string {
@ -110,8 +110,8 @@ func (f *FileInfoMock) Path() string {
} }
func (f *FileInfoMock) Size() int64 { func (f *FileInfoMock) Size() int64 {
if f != nil && f.sizeFn != nil { if f != nil && f.SizeFn != nil {
return f.sizeFn() return f.SizeFn()
} }
return int64(fileInfoSize) return int64(fileInfoSize)
@ -122,18 +122,18 @@ func (f *FileInfoMock) ModTime() time.Time {
} }
func (f *FileInfoMock) IsDir() bool { func (f *FileInfoMock) IsDir() bool {
if f != nil && f.isDirFn != nil { if f != nil && f.IsDirFn != nil {
return f.isDirFn() return f.IsDirFn()
} }
return true return true
} }
type FileWriterMock struct { type FileWriterMock struct {
writeFn func([]byte) (int, error) WriteFn func([]byte) (int, error)
cancelFn func() error CancelFn func() error
commitFn func() error CommitFn func() error
closeFn func() error CloseFn func() error
} }
func (f *FileWriterMock) Size() int64 { func (f *FileWriterMock) Size() int64 {
@ -141,117 +141,117 @@ func (f *FileWriterMock) Size() int64 {
} }
func (f *FileWriterMock) Cancel() error { func (f *FileWriterMock) Cancel() error {
if f != nil && f.cancelFn != nil { if f != nil && f.CancelFn != nil {
return f.cancelFn() return f.CancelFn()
} }
return nil return nil
} }
func (f *FileWriterMock) Commit() error { func (f *FileWriterMock) Commit() error {
if f != nil && f.commitFn != nil { if f != nil && f.CommitFn != nil {
return f.commitFn() return f.CommitFn()
} }
return nil return nil
} }
func (f *FileWriterMock) Write(p []byte) (int, error) { func (f *FileWriterMock) Write(p []byte) (int, error) {
if f != nil && f.writeFn != nil { if f != nil && f.WriteFn != nil {
return f.writeFn(p) return f.WriteFn(p)
} }
return 10, nil return 10, nil
} }
func (f *FileWriterMock) Close() error { func (f *FileWriterMock) Close() error {
if f != nil && f.closeFn != nil { if f != nil && f.CloseFn != nil {
return f.closeFn() return f.CloseFn()
} }
return nil return nil
} }
type StorageDriverMock struct { type StorageDriverMock struct {
nameFn func() string NameFn func() string
getContentFn func(ctx context.Context, path string) ([]byte, error) GetContentFn func(ctx context.Context, path string) ([]byte, error)
putContentFn func(ctx context.Context, path string, content []byte) error PutContentFn func(ctx context.Context, path string, content []byte) error
readerFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, 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) WriterFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error)
statFn func(ctx context.Context, path string) (driver.FileInfo, error) StatFn func(ctx context.Context, path string) (driver.FileInfo, error)
listFn func(ctx context.Context, path string) ([]string, error) ListFn func(ctx context.Context, path string) ([]string, error)
moveFn func(ctx context.Context, sourcePath, destPath string) error MoveFn func(ctx context.Context, sourcePath, destPath string) error
deleteFn func(ctx context.Context, path string) error DeleteFn func(ctx context.Context, path string) error
walkFn func(ctx context.Context, path string, f driver.WalkFn) error WalkFn func(ctx context.Context, path string, f driver.WalkFn) error
} }
func (s *StorageDriverMock) Name() string { func (s *StorageDriverMock) Name() string {
if s != nil && s.nameFn != nil { if s != nil && s.NameFn != nil {
return s.nameFn() return s.NameFn()
} }
return "" return ""
} }
func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) { func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) {
if s != nil && s.getContentFn != nil { if s != nil && s.GetContentFn != nil {
return s.getContentFn(ctx, path) return s.GetContentFn(ctx, path)
} }
return []byte{}, nil return []byte{}, nil
} }
func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error { func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error {
if s != nil && s.putContentFn != nil { if s != nil && s.PutContentFn != nil {
return s.putContentFn(ctx, path, content) return s.PutContentFn(ctx, path, content)
} }
return nil return nil
} }
func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) { func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
if s != nil && s.readerFn != nil { if s != nil && s.ReaderFn != nil {
return s.readerFn(ctx, path, offset) return s.ReaderFn(ctx, path, offset)
} }
return ioutil.NopCloser(strings.NewReader("")), nil return ioutil.NopCloser(strings.NewReader("")), nil
} }
func (s *StorageDriverMock) Writer(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) { func (s *StorageDriverMock) Writer(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
if s != nil && s.writerFn != nil { if s != nil && s.WriterFn != nil {
return s.writerFn(ctx, path, isAppend) return s.WriterFn(ctx, path, isAppend)
} }
return &FileWriterMock{}, nil return &FileWriterMock{}, nil
} }
func (s *StorageDriverMock) Stat(ctx context.Context, path string) (driver.FileInfo, error) { func (s *StorageDriverMock) Stat(ctx context.Context, path string) (driver.FileInfo, error) {
if s != nil && s.statFn != nil { if s != nil && s.StatFn != nil {
return s.statFn(ctx, path) return s.StatFn(ctx, path)
} }
return &FileInfoMock{}, nil return &FileInfoMock{}, nil
} }
func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) { func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) {
if s != nil && s.listFn != nil { if s != nil && s.ListFn != nil {
return s.listFn(ctx, path) return s.ListFn(ctx, path)
} }
return []string{"a"}, nil return []string{"a"}, nil
} }
func (s *StorageDriverMock) Move(ctx context.Context, sourcePath, destPath string) error { func (s *StorageDriverMock) Move(ctx context.Context, sourcePath, destPath string) error {
if s != nil && s.moveFn != nil { if s != nil && s.MoveFn != nil {
return s.moveFn(ctx, sourcePath, destPath) return s.MoveFn(ctx, sourcePath, destPath)
} }
return nil return nil
} }
func (s *StorageDriverMock) Delete(ctx context.Context, path string) error { func (s *StorageDriverMock) Delete(ctx context.Context, path string) error {
if s != nil && s.deleteFn != nil { if s != nil && s.DeleteFn != nil {
return s.deleteFn(ctx, path) return s.DeleteFn(ctx, path)
} }
return nil 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 { func (s *StorageDriverMock) Walk(ctx context.Context, path string, f driver.WalkFn) error {
if s != nil && s.walkFn != nil { if s != nil && s.WalkFn != nil {
return s.walkFn(ctx, path, f) return s.WalkFn(ctx, path, f)
} }
return nil return nil
@ -458,31 +458,31 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test storage driver errors", t, func(c C) { Convey("Test storage driver errors", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return []string{testImage}, errS3
}, },
moveFn: func(ctx context.Context, sourcePath, destPath string) error { MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
return errS3 return errS3
}, },
getContentFn: func(ctx context.Context, path string) ([]byte, error) { GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
return []byte{}, errS3 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 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 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 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 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 return &FileInfoMock{}, errS3
}, },
deleteFn: func(ctx context.Context, path string) error { DeleteFn: func(ctx context.Context, path string) error {
return errS3 return errS3
}, },
}) })
@ -532,7 +532,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return []string{testImage, testImage}, errS3
}, },
}) })
@ -541,10 +541,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 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 return nil, errS3
}, },
}) })
@ -555,10 +555,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test ValidateRepo2", t, func(c C) { Convey("Test ValidateRepo2", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 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 return &FileInfoMock{}, nil
}, },
}) })
@ -568,13 +568,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test ValidateRepo3", t, func(c C) { Convey("Test ValidateRepo3", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 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 return &FileInfoMock{}, nil
}, },
getContentFn: func(ctx context.Context, path string) ([]byte, error) { GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
return []byte{}, errS3 return []byte{}, errS3
}, },
}) })
@ -585,13 +585,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test ValidateRepo4", t, func(c C) { Convey("Test ValidateRepo4", t, func(c C) {
ociLayout := []byte(`{"imageLayoutVersion": "9.9.9"}`) ociLayout := []byte(`{"imageLayoutVersion": "9.9.9"}`)
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 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 return &FileInfoMock{}, nil
}, },
getContentFn: func(ctx context.Context, path string) ([]byte, error) { GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
return ociLayout, nil return ociLayout, nil
}, },
}) })
@ -601,7 +601,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test GetRepositories", t, func(c C) { Convey("Test GetRepositories", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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)) return f(new(FileInfoMock))
}, },
}) })
@ -612,7 +612,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test DeleteImageManifest", t, func(c C) { Convey("Test DeleteImageManifest", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return []byte{}, errS3
}, },
}) })
@ -628,7 +628,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test NewBlobUpload", t, func(c C) { Convey("Test NewBlobUpload", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return errS3
}, },
}) })
@ -638,7 +638,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test GetBlobUpload", t, func(c C) { Convey("Test GetBlobUpload", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return &FileInfoMock{}, errS3
}, },
}) })
@ -648,7 +648,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test PutBlobChunkStreamed", t, func(c C) { Convey("Test PutBlobChunkStreamed", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return &FileWriterMock{}, errS3
}, },
}) })
@ -658,8 +658,8 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test PutBlobChunkStreamed2", t, func(c C) { Convey("Test PutBlobChunkStreamed2", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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) { return &FileWriterMock{WriteFn: func(b []byte) (int, error) {
return 0, errS3 return 0, errS3
}}, nil }}, nil
}, },
@ -670,7 +670,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test PutBlobChunk", t, func(c C) { Convey("Test PutBlobChunk", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return &FileWriterMock{}, errS3
}, },
}) })
@ -680,12 +680,12 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test PutBlobChunk2", t, func(c C) { Convey("Test PutBlobChunk2", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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{ return &FileWriterMock{
writeFn: func(b []byte) (int, error) { WriteFn: func(b []byte) (int, error) {
return 0, errS3 return 0, errS3
}, },
cancelFn: func() error { CancelFn: func() error {
return errS3 return errS3
}, },
}, nil }, nil
@ -697,9 +697,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test PutBlobChunk3", t, func(c C) { Convey("Test PutBlobChunk3", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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{ return &FileWriterMock{
writeFn: func(b []byte) (int, error) { WriteFn: func(b []byte) (int, error) {
return 0, errS3 return 0, errS3
}, },
}, nil }, nil
@ -711,9 +711,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FinishBlobUpload", t, func(c C) { Convey("Test FinishBlobUpload", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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{ return &FileWriterMock{
commitFn: func() error { CommitFn: func() error {
return errS3 return errS3
}, },
}, nil }, nil
@ -726,9 +726,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FinishBlobUpload2", t, func(c C) { Convey("Test FinishBlobUpload2", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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{ return &FileWriterMock{
closeFn: func() error { CloseFn: func() error {
return errS3 return errS3
}, },
}, nil }, nil
@ -741,7 +741,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FinishBlobUpload3", t, func(c C) { Convey("Test FinishBlobUpload3", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return nil, errS3
}, },
}) })
@ -752,7 +752,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FinishBlobUpload4", t, func(c C) { Convey("Test FinishBlobUpload4", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return errS3
}, },
}) })
@ -763,7 +763,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FullBlobUpload", t, func(c C) { Convey("Test FullBlobUpload", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return &FileWriterMock{}, errS3
}, },
}) })
@ -781,7 +781,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test FullBlobUpload3", t, func(c C) { Convey("Test FullBlobUpload3", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return errS3
}, },
}) })
@ -792,7 +792,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test GetBlob", t, func(c C) { Convey("Test GetBlob", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ 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 return ioutil.NopCloser(strings.NewReader("")), errS3
}, },
}) })
@ -803,7 +803,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test DeleteBlob", t, func(c C) { Convey("Test DeleteBlob", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
deleteFn: func(ctx context.Context, path string) error { DeleteFn: func(ctx context.Context, path string) error {
return errS3 return errS3
}, },
}) })
@ -814,7 +814,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
Convey("Test GetReferrers", t, func(c C) { Convey("Test GetReferrers", t, func(c C) {
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{ imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
deleteFn: func(ctx context.Context, path string) error { DeleteFn: func(ctx context.Context, path string) error {
return errS3 return errS3
}, },
}) })
@ -1128,10 +1128,10 @@ func TestS3DedupeErr(t *testing.T) {
So(err, ShouldNotBeNil) So(err, ShouldNotBeNil)
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 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 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) { Convey("Test DedupeBlob - error on second store.Stat()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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" { if path == "dst2" {
return driver.FileInfoInternal{}, errS3 return driver.FileInfoInternal{}, errS3
} }
@ -1168,10 +1168,10 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test DedupeBlob - error on store.PutContent()", t, func(c C) { Convey("Test DedupeBlob - error on store.PutContent()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 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 return nil, nil
}, },
}) })
@ -1187,10 +1187,10 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test DedupeBlob - error on store.Delete()", t, func(c C) { Convey("Test DedupeBlob - error on store.Delete()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
deleteFn: func(ctx context.Context, path string) error { DeleteFn: func(ctx context.Context, path string) error {
return errS3 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 return nil, nil
}, },
}) })
@ -1206,13 +1206,13 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test copyBlob() - error on initRepo()", t, func(c C) { Convey("Test copyBlob() - error on initRepo()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 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 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 return &FileWriterMock{}, errS3
}, },
}) })
@ -1230,10 +1230,10 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test copyBlob() - error on store.PutContent()", t, func(c C) { Convey("Test copyBlob() - error on store.PutContent()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 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 return driver.FileInfoInternal{}, errS3
}, },
}) })
@ -1251,7 +1251,7 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test copyBlob() - error on store.Stat()", t, func(c C) { Convey("Test copyBlob() - error on store.Stat()", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 return driver.FileInfoInternal{}, errS3
}, },
}) })
@ -1290,7 +1290,7 @@ func TestS3DedupeErr(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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") { if strings.Contains(path, "repo1/dst1") {
return driver.FileInfoInternal{}, driver.PathNotFoundError{} return driver.FileInfoInternal{}, driver.PathNotFoundError{}
} }
@ -1327,14 +1327,14 @@ func TestS3DedupeErr(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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{ return &FileInfoMock{
sizeFn: func() int64 { SizeFn: func() int64 {
return 0 return 0
}, },
}, nil }, 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") { if strings.Contains(path, "repo1/dst1") {
return ioutil.NopCloser(strings.NewReader("")), errS3 return ioutil.NopCloser(strings.NewReader("")), errS3
} }
@ -1356,14 +1356,14 @@ func TestS3DedupeErr(t *testing.T) {
blobPath := path.Join(testDir, "repo/blobs/sha256", hash) blobPath := path.Join(testDir, "repo/blobs/sha256", hash)
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 { if destPath == blobPath {
return nil return nil
} }
return errS3 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 { if path != blobPath {
return nil, errS3 return nil, errS3
} }
@ -1385,7 +1385,7 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test FullBlobUpload", t, func(c C) { Convey("Test FullBlobUpload", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 return errS3
}, },
}) })
@ -1397,7 +1397,7 @@ func TestS3DedupeErr(t *testing.T) {
Convey("Test FinishBlobUpload", t, func(c C) { Convey("Test FinishBlobUpload", t, func(c C) {
tdir := t.TempDir() tdir := t.TempDir()
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 return errS3
}, },
}) })
@ -1419,7 +1419,7 @@ func TestInjectDedupe(t *testing.T) {
Convey("Inject errors in DedupeBlob function", t, func() { Convey("Inject errors in DedupeBlob function", t, func() {
imgStore := createMockStorage(testDir, tdir, true, &StorageDriverMock{ 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 return &FileInfoMock{}, errS3
}, },
}) })

View file

@ -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)
}
}

121
pkg/test/mocks/oci_mock.go Normal file
View file

@ -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
}

View file

@ -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
}