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:
parent
a31869f270
commit
7e3d063319
20 changed files with 3597 additions and 562 deletions
|
@ -4,3 +4,4 @@ ignore:
|
|||
- "./pkg/extensions/minimal.go"
|
||||
- "./pkg/cli/minimal.go"
|
||||
- "./cmd/zb/*.go"
|
||||
- "./pkg/test/mocks/*.go"
|
||||
|
|
8
go.mod
8
go.mod
|
@ -361,16 +361,16 @@ require (
|
|||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/net v0.0.0-20220524220425-1d687d428aca // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 // indirect
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
|
||||
golang.org/x/tools v0.1.10 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||
golang.org/x/tools v0.1.11 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||
google.golang.org/api v0.81.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
|
||||
|
|
14
go.sum
14
go.sum
|
@ -141,8 +141,6 @@ github.com/Azure/azure-sdk-for-go v63.3.0+incompatible h1:INepVujzUrmArRZjDLHbtE
|
|||
github.com/Azure/azure-sdk-for-go v63.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
|
||||
github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU=
|
||||
github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU=
|
||||
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
|
||||
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
|
||||
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
|
||||
github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
|
||||
|
@ -2792,8 +2790,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -3099,8 +3098,9 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew=
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -3241,15 +3241,17 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,8 +6,10 @@ package common_test
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
@ -30,12 +32,15 @@ import (
|
|||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
. "zotregistry.io/zot/pkg/test"
|
||||
"zotregistry.io/zot/pkg/test/mocks"
|
||||
)
|
||||
|
||||
const (
|
||||
graphqlQueryPrefix = constants.ExtSearchPrefix
|
||||
)
|
||||
|
||||
var ErrTestError = errors.New("test error")
|
||||
|
||||
// nolint:gochecknoglobals
|
||||
var (
|
||||
rootDir string
|
||||
|
@ -52,6 +57,50 @@ type ExpandedRepoInfoResp struct {
|
|||
Errors []ErrorGQL `json:"errors"`
|
||||
}
|
||||
|
||||
type GlobalSearchResultResp struct {
|
||||
GlobalSearchResult GlobalSearchResult `json:"data"`
|
||||
Errors []ErrorGQL `json:"errors"`
|
||||
}
|
||||
|
||||
type GlobalSearchResult struct {
|
||||
GlobalSearch GlobalSearch `json:"globalSearch"`
|
||||
}
|
||||
type GlobalSearch struct {
|
||||
Images []ImageSummary `json:"images"`
|
||||
Repos []RepoSummary `json:"repos"`
|
||||
Layers []LayerSummary `json:"layers"`
|
||||
}
|
||||
|
||||
type ImageSummary struct {
|
||||
RepoName string `json:"repoName"`
|
||||
Tag string `json:"tag"`
|
||||
LastUpdated time.Time `json:"lastUpdated"`
|
||||
Size string `json:"size"`
|
||||
Platform OsArch `json:"platform"`
|
||||
Vendor string `json:"vendor"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type RepoSummary struct {
|
||||
Name string `json:"name"`
|
||||
LastUpdated time.Time `json:"lastUpdated"`
|
||||
Size string `json:"size"`
|
||||
Platforms []OsArch `json:"platforms"`
|
||||
Vendors []string `json:"vendors"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type LayerSummary struct {
|
||||
Size string `json:"size"`
|
||||
Digest string `json:"digest"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type OsArch struct {
|
||||
Os string `json:"os"`
|
||||
Arch string `json:"arch"`
|
||||
}
|
||||
|
||||
type ExpandedRepoInfo struct {
|
||||
RepoInfo common.RepoInfo `json:"expandedRepoInfo"`
|
||||
}
|
||||
|
@ -210,7 +259,7 @@ func TestImageFormat(t *testing.T) {
|
|||
metrics := monitoring.NewMetricsServer(false, log)
|
||||
defaultStore := storage.NewImageStore(dbDir, false, storage.DefaultGCDelay, false, false, log, metrics)
|
||||
storeController := storage.StoreController{DefaultStore: defaultStore}
|
||||
olu := common.NewOciLayoutUtils(storeController, log)
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log)
|
||||
|
||||
isValidImage, err := olu.IsValidImageFormat("zot-test")
|
||||
So(err, ShouldBeNil)
|
||||
|
@ -671,3 +720,404 @@ func TestUtilsMethod(t *testing.T) {
|
|||
So(dir, ShouldEqual, subRootDir)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGlobalSearch(t *testing.T) {
|
||||
Convey("Test utils", t, func() {
|
||||
subpath := "/a"
|
||||
|
||||
err := testSetup(t, subpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
port := GetFreePort()
|
||||
baseURL := GetBaseURL(port)
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = rootDir
|
||||
conf.Storage.SubPaths = make(map[string]config.StorageConfig)
|
||||
conf.Storage.SubPaths[subpath] = config.StorageConfig{RootDirectory: subRootDir}
|
||||
defaultVal := true
|
||||
conf.Extensions = &extconf.ExtensionConfig{
|
||||
Search: &extconf.SearchConfig{Enable: &defaultVal},
|
||||
}
|
||||
|
||||
conf.Extensions.Search.CVE = nil
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
|
||||
go func() {
|
||||
// this blocks
|
||||
if err := ctlr.Run(context.Background()); err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// wait till ready
|
||||
for {
|
||||
_, err := resty.R().Get(baseURL)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// shut down server
|
||||
|
||||
defer func() {
|
||||
ctx := context.Background()
|
||||
_ = ctlr.Server.Shutdown(ctx)
|
||||
}()
|
||||
|
||||
query := `
|
||||
{
|
||||
GlobalSearch(query:""){
|
||||
Images {
|
||||
RepoName
|
||||
Tag
|
||||
LastUpdated
|
||||
Size
|
||||
Score
|
||||
}
|
||||
Repos {
|
||||
Name
|
||||
LastUpdated
|
||||
Size
|
||||
Platforms {
|
||||
Os
|
||||
Arch
|
||||
}
|
||||
Vendors
|
||||
Score
|
||||
}
|
||||
Layers {
|
||||
Digest
|
||||
Size
|
||||
}
|
||||
}
|
||||
}`
|
||||
resp, err := resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query))
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
responseStruct := &GlobalSearchResultResp{}
|
||||
|
||||
err = json.Unmarshal(resp.Body(), responseStruct)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(responseStruct.GlobalSearchResult.GlobalSearch.Images, ShouldNotBeNil)
|
||||
So(len(responseStruct.GlobalSearchResult.GlobalSearch.Images), ShouldNotBeEmpty)
|
||||
So(len(responseStruct.GlobalSearchResult.GlobalSearch.Repos), ShouldNotBeEmpty)
|
||||
So(len(responseStruct.GlobalSearchResult.GlobalSearch.Layers), ShouldNotBeEmpty)
|
||||
|
||||
// GetRepositories fail
|
||||
|
||||
err = os.Chmod(rootDir, 0o333)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + graphqlQueryPrefix + "?query=" + url.QueryEscape(query))
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
responseStruct = &GlobalSearchResultResp{}
|
||||
err = json.Unmarshal(resp.Body(), responseStruct)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(responseStruct.Errors, ShouldNotBeEmpty)
|
||||
err = os.Chmod(rootDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBaseOciLayoutUtils(t *testing.T) {
|
||||
manifestDigest := "sha256:adf3bb6cc81f8bd6a9d5233be5f0c1a4f1e3ed1cf5bbdfad7708cc8d4099b741"
|
||||
|
||||
Convey("GetImageManifestSize fail", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
size := olu.GetImageManifestSize("", "")
|
||||
So(size, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageConfigSize: fail GetImageBlobManifest", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
size := olu.GetImageConfigSize("", "")
|
||||
So(size, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageConfigSize: config GetBlobContent fail", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
if digest == manifestDigest {
|
||||
return []byte{}, ErrTestError
|
||||
}
|
||||
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": manifestDigest,
|
||||
"size": 1476
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc",
|
||||
"size": 76097157
|
||||
}
|
||||
]
|
||||
}`), nil
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
size := olu.GetImageConfigSize("", "")
|
||||
So(size, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetRepoLastUpdated: config GetBlobContent fail", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetIndexContentFn: func(repo string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
_, err := olu.GetRepoLastUpdated("")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("GetImageLastUpdated: GetImageBlobManifest fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
time := olu.GetImageLastUpdated("", "")
|
||||
So(time, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageLastUpdated: GetImageInfo fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
if digest == manifestDigest {
|
||||
return []byte{}, ErrTestError
|
||||
}
|
||||
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": manifestDigest,
|
||||
"size": 1476
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc",
|
||||
"size": 76097157
|
||||
}
|
||||
]
|
||||
}`), nil
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
time := olu.GetImageLastUpdated("", "")
|
||||
So(time, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageLastUpdated: GetImageInfo history is empty", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
if digest == manifestDigest {
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"created": "2020-11-14T00:20:04.644613188Z",
|
||||
"architecture": "amd64",
|
||||
"os": "linux",
|
||||
"config": {
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
],
|
||||
"Cmd": [
|
||||
"/bin/bash"
|
||||
],
|
||||
"Labels": {
|
||||
}
|
||||
},
|
||||
"rootfs": {
|
||||
"type": "layers",
|
||||
"diff_ids": [
|
||||
"sha256:174f5685490326fc0a1c0f5570b8663732189b327007e47ff13d2ca59673db02"
|
||||
]
|
||||
},
|
||||
"history": [
|
||||
]
|
||||
}
|
||||
`), nil
|
||||
}
|
||||
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": manifestDigest,
|
||||
"size": 1476
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc",
|
||||
"size": 76097157
|
||||
}
|
||||
]
|
||||
}`), nil
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
time := olu.GetImageLastUpdated("", "")
|
||||
So(time, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImagePlatform: GetImageBlobManifest fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
os, arch := olu.GetImagePlatform("", "")
|
||||
So(os, ShouldBeZeroValue)
|
||||
So(arch, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImagePlatform: GetImageInfo fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
if digest == manifestDigest {
|
||||
return []byte{}, ErrTestError
|
||||
}
|
||||
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": manifestDigest,
|
||||
"size": 1476
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc",
|
||||
"size": 76097157
|
||||
}
|
||||
]
|
||||
}`), nil
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
os, arch := olu.GetImagePlatform("", "")
|
||||
So(os, ShouldBeZeroValue)
|
||||
So(arch, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageVendor: GetImageBlobManifest fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
return []byte{}, ErrTestError
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
vendor := olu.GetImageVendor("", "")
|
||||
So(vendor, ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("GetImageVendor: GetImageInfo fails", t, func() {
|
||||
mockStoreController := mocks.MockedImageStore{
|
||||
GetBlobContentFn: func(repo, digest string) ([]byte, error) {
|
||||
if digest == manifestDigest {
|
||||
return []byte{}, ErrTestError
|
||||
}
|
||||
|
||||
return []byte(
|
||||
`
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": manifestDigest,
|
||||
"size": 1476
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc",
|
||||
"size": 76097157
|
||||
}
|
||||
]
|
||||
}`), nil
|
||||
},
|
||||
}
|
||||
|
||||
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||
|
||||
vendor := olu.GetImageVendor("", "")
|
||||
So(vendor, ShouldBeZeroValue)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -20,8 +20,23 @@ import (
|
|||
"zotregistry.io/zot/pkg/storage"
|
||||
)
|
||||
|
||||
type OciLayoutUtils interface {
|
||||
GetImageManifests(image string) ([]ispec.Descriptor, error)
|
||||
GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error)
|
||||
GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error)
|
||||
IsValidImageFormat(image string) (bool, error)
|
||||
GetImageTagsWithTimestamp(repo string) ([]TagInfo, error)
|
||||
GetImageLastUpdated(repo string, manifestDigest godigest.Digest) time.Time
|
||||
GetImagePlatform(repo string, manifestDigest godigest.Digest) (string, string)
|
||||
GetImageVendor(repo string, manifestDigest godigest.Digest) string
|
||||
GetImageManifestSize(repo string, manifestDigest godigest.Digest) int64
|
||||
GetImageConfigSize(repo string, manifestDigest godigest.Digest) int64
|
||||
GetRepoLastUpdated(repo string) (time.Time, error)
|
||||
GetExpandedRepoInfo(name string) (RepoInfo, error)
|
||||
}
|
||||
|
||||
// OciLayoutInfo ...
|
||||
type OciLayoutUtils struct {
|
||||
type BaseOciLayoutUtils struct {
|
||||
Log log.Logger
|
||||
StoreController storage.StoreController
|
||||
}
|
||||
|
@ -42,13 +57,13 @@ type Layer struct {
|
|||
Digest string `json:"digest"`
|
||||
}
|
||||
|
||||
// NewOciLayoutUtils initializes a new OciLayoutUtils object.
|
||||
func NewOciLayoutUtils(storeController storage.StoreController, log log.Logger) *OciLayoutUtils {
|
||||
return &OciLayoutUtils{Log: log, StoreController: storeController}
|
||||
// NewBaseOciLayoutUtils initializes a new OciLayoutUtils object.
|
||||
func NewBaseOciLayoutUtils(storeController storage.StoreController, log log.Logger) *BaseOciLayoutUtils {
|
||||
return &BaseOciLayoutUtils{Log: log, StoreController: storeController}
|
||||
}
|
||||
|
||||
// Below method will return image path including root dir, root dir is determined by splitting.
|
||||
func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) {
|
||||
func (olu BaseOciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, error) {
|
||||
imageStore := olu.StoreController.GetImageStore(image)
|
||||
|
||||
buf, err := imageStore.GetIndexContent(image)
|
||||
|
@ -76,7 +91,7 @@ func (olu OciLayoutUtils) GetImageManifests(image string) ([]ispec.Descriptor, e
|
|||
}
|
||||
|
||||
//nolint: interfacer
|
||||
func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) {
|
||||
func (olu BaseOciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.Digest) (v1.Manifest, error) {
|
||||
var blobIndex v1.Manifest
|
||||
|
||||
imageStore := olu.StoreController.GetImageStore(imageDir)
|
||||
|
@ -98,7 +113,7 @@ func (olu OciLayoutUtils) GetImageBlobManifest(imageDir string, digest godigest.
|
|||
}
|
||||
|
||||
//nolint: interfacer
|
||||
func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) {
|
||||
func (olu BaseOciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Image, error) {
|
||||
var imageInfo ispec.Image
|
||||
|
||||
imageStore := olu.StoreController.GetImageStore(imageDir)
|
||||
|
@ -119,7 +134,7 @@ func (olu OciLayoutUtils) GetImageInfo(imageDir string, hash v1.Hash) (ispec.Ima
|
|||
return imageInfo, err
|
||||
}
|
||||
|
||||
func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
|
||||
func (olu BaseOciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
|
||||
imageDir, inputTag := GetImageDirAndTag(image)
|
||||
|
||||
manifests, err := olu.GetImageManifests(imageDir)
|
||||
|
@ -158,7 +173,7 @@ func (olu OciLayoutUtils) IsValidImageFormat(image string) (bool, error) {
|
|||
}
|
||||
|
||||
// GetImageTagsWithTimestamp returns a list of image tags with timestamp available in the specified repository.
|
||||
func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) {
|
||||
func (olu BaseOciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, error) {
|
||||
tagsInfo := make([]TagInfo, 0)
|
||||
|
||||
manifests, err := olu.GetImageManifests(repo)
|
||||
|
@ -203,7 +218,7 @@ func (olu OciLayoutUtils) GetImageTagsWithTimestamp(repo string) ([]TagInfo, err
|
|||
}
|
||||
|
||||
// check notary signature corresponding to repo name, manifest digest and mediatype.
|
||||
func (olu OciLayoutUtils) checkNotarySignature(name string, digest godigest.Digest) bool {
|
||||
func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.Digest) bool {
|
||||
imageStore := olu.StoreController.GetImageStore(name)
|
||||
mediaType := notreg.ArtifactTypeNotation
|
||||
|
||||
|
@ -219,7 +234,7 @@ func (olu OciLayoutUtils) checkNotarySignature(name string, digest godigest.Dige
|
|||
}
|
||||
|
||||
// check cosign signature corresponding to manifest.
|
||||
func (olu OciLayoutUtils) checkCosignSignature(name string, digest godigest.Digest) bool {
|
||||
func (olu BaseOciLayoutUtils) checkCosignSignature(name string, digest godigest.Digest) bool {
|
||||
imageStore := olu.StoreController.GetImageStore(name)
|
||||
|
||||
// if manifest is signed using cosign mechanism, cosign adds a new manifest.
|
||||
|
@ -240,7 +255,7 @@ func (olu OciLayoutUtils) checkCosignSignature(name string, digest godigest.Dige
|
|||
// checks if manifest is signed or not
|
||||
// checks for notary or cosign signature
|
||||
// if cosign signature found it does not looks for notary signature.
|
||||
func (olu OciLayoutUtils) checkManifestSignature(name string, digest godigest.Digest) bool {
|
||||
func (olu BaseOciLayoutUtils) checkManifestSignature(name string, digest godigest.Digest) bool {
|
||||
if !olu.checkCosignSignature(name, digest) {
|
||||
return olu.checkNotarySignature(name, digest)
|
||||
}
|
||||
|
@ -248,7 +263,112 @@ func (olu OciLayoutUtils) checkManifestSignature(name string, digest godigest.Di
|
|||
return true
|
||||
}
|
||||
|
||||
func (olu OciLayoutUtils) GetExpandedRepoInfo(name string) (RepoInfo, error) {
|
||||
func (olu BaseOciLayoutUtils) GetImageLastUpdated(repo string, manifestDigest godigest.Digest) time.Time {
|
||||
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("unable to read image blob")
|
||||
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
imageInfo, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("unable to read image info")
|
||||
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
var timeStamp time.Time
|
||||
|
||||
if len(imageInfo.History) != 0 {
|
||||
timeStamp = *imageInfo.History[0].Created
|
||||
} else {
|
||||
timeStamp = time.Time{}
|
||||
}
|
||||
|
||||
return timeStamp
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetImagePlatform(repo string, manifestDigest godigest.Digest) (
|
||||
string, string,
|
||||
) {
|
||||
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("can't get image blob manifest")
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
imageConfig, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("extension api: error reading image config")
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return imageConfig.OS, imageConfig.Architecture
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetImageVendor(repo string, manifestDigest godigest.Digest) string {
|
||||
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("can't get image blob manifest")
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
imageConfig, err := olu.GetImageInfo(repo, imageBlobManifest.Config.Digest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("extension api: error reading image config")
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
return imageConfig.Config.Labels["vendor"]
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetImageManifestSize(repo string, manifestDigest godigest.Digest) int64 {
|
||||
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("can't get image blob manifest")
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
return imageBlobManifest.Config.Size
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetImageConfigSize(repo string, manifestDigest godigest.Digest) int64 {
|
||||
imageBlobManifest, err := olu.GetImageBlobManifest(repo, manifestDigest)
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("can't get image blob manifest")
|
||||
|
||||
return 0
|
||||
}
|
||||
imageStore := olu.StoreController.GetImageStore(repo)
|
||||
|
||||
buf, err := imageStore.GetBlobContent(repo, imageBlobManifest.Config.Digest.String())
|
||||
if err != nil {
|
||||
olu.Log.Error().Err(err).Msg("error when getting blob content")
|
||||
|
||||
return int64(len(buf))
|
||||
}
|
||||
|
||||
return int64(len(buf))
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetRepoLastUpdated(repo string) (time.Time, error) {
|
||||
tagsInfo, err := olu.GetImageTagsWithTimestamp(repo)
|
||||
if err != nil || len(tagsInfo) == 0 {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
latestTag := GetLatestTag(tagsInfo)
|
||||
|
||||
return latestTag.Timestamp, nil
|
||||
}
|
||||
|
||||
func (olu BaseOciLayoutUtils) GetExpandedRepoInfo(name string) (RepoInfo, error) {
|
||||
repo := RepoInfo{}
|
||||
|
||||
manifests := make([]Manifest, 0)
|
||||
|
|
|
@ -78,7 +78,7 @@ func ScanImage(ctx *cli.Context) (report.Report, error) {
|
|||
|
||||
func GetCVEInfo(storeController storage.StoreController, log log.Logger) (*CveInfo, error) {
|
||||
cveController := CveTrivyController{}
|
||||
layoutUtils := common.NewOciLayoutUtils(storeController, log)
|
||||
layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
|
||||
|
||||
subCveConfig := make(map[string]*TrivyCtx)
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ func testSetup() error {
|
|||
|
||||
storeController := storage.StoreController{DefaultStore: storage.NewImageStore(dir, false, storage.DefaultGCDelay, false, false, log, metrics)}
|
||||
|
||||
layoutUtils := common.NewOciLayoutUtils(storeController, log)
|
||||
layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
|
||||
|
||||
cve = &cveinfo.CveInfo{Log: log, StoreController: storeController, LayoutUtils: layoutUtils}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ type CveInfo struct {
|
|||
Log log.Logger
|
||||
CveTrivyController CveTrivyController
|
||||
StoreController storage.StoreController
|
||||
LayoutUtils *common.OciLayoutUtils
|
||||
LayoutUtils *common.BaseOciLayoutUtils
|
||||
}
|
||||
|
||||
type CveTrivyController struct {
|
||||
|
|
|
@ -12,12 +12,12 @@ import (
|
|||
// DigestInfo implements searching by manifes/config/layer digest.
|
||||
type DigestInfo struct {
|
||||
Log log.Logger
|
||||
LayoutUtils *common.OciLayoutUtils
|
||||
LayoutUtils *common.BaseOciLayoutUtils
|
||||
}
|
||||
|
||||
// NewDigestInfo initializes a new DigestInfo object.
|
||||
func NewDigestInfo(storeController storage.StoreController, log log.Logger) *DigestInfo {
|
||||
layoutUtils := common.NewOciLayoutUtils(storeController, log)
|
||||
layoutUtils := common.NewBaseOciLayoutUtils(storeController, log)
|
||||
|
||||
return &DigestInfo{Log: log, LayoutUtils: layoutUtils}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -19,6 +19,12 @@ type CVEResultForImage struct {
|
|||
CVEList []*Cve `json:"CVEList"`
|
||||
}
|
||||
|
||||
type GlobalSearchResult struct {
|
||||
Images []*ImageSummary `json:"Images"`
|
||||
Repos []*RepoSummary `json:"Repos"`
|
||||
Layers []*LayerSummary `json:"Layers"`
|
||||
}
|
||||
|
||||
type ImageInfo struct {
|
||||
Name *string `json:"Name"`
|
||||
Latest *string `json:"Latest"`
|
||||
|
@ -30,6 +36,17 @@ type ImageInfo struct {
|
|||
Labels *string `json:"Labels"`
|
||||
}
|
||||
|
||||
type ImageSummary struct {
|
||||
RepoName *string `json:"RepoName"`
|
||||
Tag *string `json:"Tag"`
|
||||
LastUpdated *time.Time `json:"LastUpdated"`
|
||||
IsSigned *bool `json:"IsSigned"`
|
||||
Size *string `json:"Size"`
|
||||
Platform *OsArch `json:"Platform"`
|
||||
Vendor *string `json:"Vendor"`
|
||||
Score *int `json:"Score"`
|
||||
}
|
||||
|
||||
type ImgResultForCve struct {
|
||||
Name *string `json:"Name"`
|
||||
Tags []*string `json:"Tags"`
|
||||
|
@ -49,6 +66,12 @@ type LayerInfo struct {
|
|||
Digest *string `json:"Digest"`
|
||||
}
|
||||
|
||||
type LayerSummary struct {
|
||||
Size *string `json:"Size"`
|
||||
Digest *string `json:"Digest"`
|
||||
Score *int `json:"Score"`
|
||||
}
|
||||
|
||||
type ManifestInfo struct {
|
||||
Digest *string `json:"Digest"`
|
||||
Tag *string `json:"Tag"`
|
||||
|
@ -56,6 +79,11 @@ type ManifestInfo struct {
|
|||
Layers []*LayerInfo `json:"Layers"`
|
||||
}
|
||||
|
||||
type OsArch struct {
|
||||
Os *string `json:"Os"`
|
||||
Arch *string `json:"Arch"`
|
||||
}
|
||||
|
||||
type PackageInfo struct {
|
||||
Name *string `json:"Name"`
|
||||
InstalledVersion *string `json:"InstalledVersion"`
|
||||
|
@ -66,6 +94,15 @@ type RepoInfo struct {
|
|||
Manifests []*ManifestInfo `json:"Manifests"`
|
||||
}
|
||||
|
||||
type RepoSummary struct {
|
||||
Name *string `json:"Name"`
|
||||
LastUpdated *time.Time `json:"LastUpdated"`
|
||||
Size *string `json:"Size"`
|
||||
Platforms []*OsArch `json:"Platforms"`
|
||||
Vendors []*string `json:"Vendors"`
|
||||
Score *int `json:"Score"`
|
||||
}
|
||||
|
||||
type TagInfo struct {
|
||||
Name *string `json:"Name"`
|
||||
Digest *string `json:"Digest"`
|
||||
|
|
|
@ -5,7 +5,9 @@ package search
|
|||
// It serves as dependency injection for your app, add any dependencies you require here.
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"zotregistry.io/zot/pkg/log" // nolint: gci
|
||||
|
@ -123,7 +125,7 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*
|
|||
r.log.Info().Msg("no repositories found")
|
||||
}
|
||||
|
||||
layoutUtils := common.NewOciLayoutUtils(r.storeController, r.log)
|
||||
layoutUtils := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
||||
|
||||
for _, repo := range repoList {
|
||||
tagsInfo, err := layoutUtils.GetImageTagsWithTimestamp(repo)
|
||||
|
@ -186,6 +188,180 @@ func (r *queryResolver) getImageListWithLatestTag(store storage.ImageStore) ([]*
|
|||
return results, nil
|
||||
}
|
||||
|
||||
func cleanQuerry(query string) string {
|
||||
query = strings.ToLower(query)
|
||||
query = strings.Replace(query, ":", " ", 1)
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
func globalSearch(repoList []string, name, tag string, olu common.OciLayoutUtils, log log.Logger) (
|
||||
[]*gql_generated.RepoSummary, []*gql_generated.ImageSummary, []*gql_generated.LayerSummary,
|
||||
) {
|
||||
repos := []*gql_generated.RepoSummary{}
|
||||
images := []*gql_generated.ImageSummary{}
|
||||
layers := []*gql_generated.LayerSummary{}
|
||||
|
||||
for _, repo := range repoList {
|
||||
repo := repo
|
||||
|
||||
// map used for dedube if 2 images reference the same blob
|
||||
repoLayerBlob2Size := make(map[string]int64, 10)
|
||||
|
||||
// made up of all manifests, configs and image layers
|
||||
repoSize := int64(0)
|
||||
|
||||
lastUpdate, err := olu.GetRepoLastUpdated(repo)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("can't find latest update timestamp for repo: %s", repo)
|
||||
}
|
||||
|
||||
tagsInfo, err := olu.GetImageTagsWithTimestamp(repo)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("can't get tags info for repo: %s", repo)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
repoInfo, err := olu.GetExpandedRepoInfo(repo)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("can't get repo info for repo: %s", repo)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
repoPlatforms := make([]*gql_generated.OsArch, 0, len(tagsInfo))
|
||||
repoVendors := make([]*string, 0, len(repoInfo.Manifests))
|
||||
|
||||
for i, manifest := range repoInfo.Manifests {
|
||||
imageLayersSize := int64(0)
|
||||
manifestSize := olu.GetImageManifestSize(repo, godigest.Digest(tagsInfo[i].Digest))
|
||||
configSize := olu.GetImageConfigSize(repo, godigest.Digest(tagsInfo[i].Digest))
|
||||
|
||||
for _, layer := range manifest.Layers {
|
||||
layer := layer
|
||||
|
||||
layerSize, err := strconv.ParseInt(layer.Size, 10, 64)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("invalid layer size")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
repoLayerBlob2Size[layer.Digest] = layerSize
|
||||
imageLayersSize += layerSize
|
||||
|
||||
// if we have a tag we won't match a layer
|
||||
if tag != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if index := strings.Index(layer.Digest, name); index != -1 {
|
||||
layers = append(layers, &gql_generated.LayerSummary{
|
||||
Digest: &layer.Digest,
|
||||
Size: &layer.Size,
|
||||
Score: &index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
imageSize := imageLayersSize + manifestSize + configSize
|
||||
repoSize += manifestSize + configSize
|
||||
|
||||
index := strings.Index(repo, name)
|
||||
matchesTag := strings.HasPrefix(manifest.Tag, tag)
|
||||
|
||||
if index != -1 {
|
||||
tag := manifest.Tag
|
||||
size := strconv.Itoa(int(imageSize))
|
||||
vendor := olu.GetImageVendor(repo, godigest.Digest(tagsInfo[i].Digest))
|
||||
lastUpdated := olu.GetImageLastUpdated(repo, godigest.Digest(tagsInfo[i].Digest))
|
||||
|
||||
isSigned := manifest.IsSigned
|
||||
// update matching score
|
||||
score := calculateImageMatchingScore(repo, index, matchesTag)
|
||||
|
||||
os, arch := olu.GetImagePlatform(repo, godigest.Digest(tagsInfo[i].Digest))
|
||||
osArch := &gql_generated.OsArch{
|
||||
Os: &os,
|
||||
Arch: &arch,
|
||||
}
|
||||
|
||||
repoPlatforms = append(repoPlatforms, osArch)
|
||||
repoVendors = append(repoVendors, &vendor)
|
||||
|
||||
images = append(images, &gql_generated.ImageSummary{
|
||||
RepoName: &repo,
|
||||
Tag: &tag,
|
||||
LastUpdated: &lastUpdated,
|
||||
IsSigned: &isSigned,
|
||||
Size: &size,
|
||||
Platform: osArch,
|
||||
Vendor: &vendor,
|
||||
Score: &score,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for layerBlob := range repoLayerBlob2Size {
|
||||
repoSize += repoLayerBlob2Size[layerBlob]
|
||||
}
|
||||
|
||||
if index := strings.Index(repo, name); index != -1 {
|
||||
repoSize := strconv.FormatInt(repoSize, 10)
|
||||
|
||||
repos = append(repos, &gql_generated.RepoSummary{
|
||||
Name: &repo,
|
||||
LastUpdated: &lastUpdate,
|
||||
Size: &repoSize,
|
||||
Platforms: repoPlatforms,
|
||||
Vendors: repoVendors,
|
||||
Score: &index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(repos, func(i, j int) bool {
|
||||
return *repos[i].Score < *repos[j].Score
|
||||
})
|
||||
|
||||
sort.Slice(images, func(i, j int) bool {
|
||||
return *images[i].Score < *images[j].Score
|
||||
})
|
||||
|
||||
sort.Slice(layers, func(i, j int) bool {
|
||||
return *layers[i].Score < *layers[j].Score
|
||||
})
|
||||
|
||||
return repos, images, layers
|
||||
}
|
||||
|
||||
// calcalculateImageMatchingScore iterated from the index of the matched string in the
|
||||
// artifact name until the beginning of the string or until delimitator "/".
|
||||
// The distance represents the score of the match.
|
||||
//
|
||||
// Example:
|
||||
// query: image
|
||||
// repos: repo/test/myimage
|
||||
// Score will be 2.
|
||||
func calculateImageMatchingScore(artefactName string, index int, matchesTag bool) int {
|
||||
score := 0
|
||||
|
||||
for index >= 1 {
|
||||
if artefactName[index-1] == '/' {
|
||||
break
|
||||
}
|
||||
index--
|
||||
score++
|
||||
}
|
||||
|
||||
if !matchesTag {
|
||||
score += 10
|
||||
}
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
func getGraphqlCompatibleTags(fixedTags []common.TagInfo) []*gql_generated.TagInfo {
|
||||
finalTagList := make([]*gql_generated.TagInfo, 0)
|
||||
|
||||
|
|
154
pkg/extensions/search/resolver_test.go
Normal file
154
pkg/extensions/search/resolver_test.go
Normal 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)
|
||||
})
|
||||
}
|
|
@ -66,6 +66,50 @@ type LayerInfo {
|
|||
Digest: String
|
||||
}
|
||||
|
||||
# Search results in all repos/images/layers
|
||||
# There will be other more structures for more detailed information
|
||||
type GlobalSearchResult {
|
||||
Images: [ImageSummary]
|
||||
Repos: [RepoSummary]
|
||||
Layers: [LayerSummary]
|
||||
}
|
||||
|
||||
# Brief on a specific image to be used in queries returning a list of images
|
||||
# We define an image as a pairing or a repo and a tag belonging to that repo
|
||||
type ImageSummary {
|
||||
RepoName: String
|
||||
Tag: String
|
||||
LastUpdated: Time
|
||||
IsSigned: Boolean
|
||||
Size: String
|
||||
Platform: OsArch
|
||||
Vendor: String
|
||||
Score: Int
|
||||
}
|
||||
|
||||
# Brief on a specific repo to be used in queries returning a list of repos
|
||||
type RepoSummary {
|
||||
Name: String
|
||||
LastUpdated: Time
|
||||
Size: String
|
||||
Platforms: [OsArch]
|
||||
Vendors: [String]
|
||||
Score: Int
|
||||
}
|
||||
|
||||
# Currently the same as LayerInfo, we can refactor later
|
||||
# For detailed information on the layer a ImageListForDigest call can be made
|
||||
type LayerSummary {
|
||||
Size: String # Int64 is not supported.
|
||||
Digest: String
|
||||
Score: Int
|
||||
}
|
||||
|
||||
type OsArch {
|
||||
Os: String
|
||||
Arch: String
|
||||
}
|
||||
|
||||
type Query {
|
||||
CVEListForImage(image: String!) :CVEResultForImage
|
||||
ImageListForCVE(id: String!) :[ImgResultForCVE]
|
||||
|
@ -73,4 +117,5 @@ type Query {
|
|||
ImageListForDigest(id: String!) :[ImgResultForDigest]
|
||||
ImageListWithLatestTag:[ImageInfo]
|
||||
ExpandedRepoInfo(repo: String!):RepoInfo
|
||||
GlobalSearch(query: String!): GlobalSearchResult
|
||||
}
|
||||
|
|
|
@ -319,7 +319,7 @@ func (r *queryResolver) ImageListWithLatestTag(ctx context.Context) ([]*gql_gene
|
|||
|
||||
// ExpandedRepoInfo is the resolver for the ExpandedRepoInfo field.
|
||||
func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql_generated.RepoInfo, error) {
|
||||
olu := common.NewOciLayoutUtils(r.storeController, r.log)
|
||||
olu := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
||||
|
||||
origRepoInfo, err := olu.GetExpandedRepoInfo(repo)
|
||||
if err != nil {
|
||||
|
@ -364,6 +364,35 @@ func (r *queryResolver) ExpandedRepoInfo(ctx context.Context, repo string) (*gql
|
|||
return repoInfo, nil
|
||||
}
|
||||
|
||||
// GlobalSearch is the resolver for the GlobalSearch field.
|
||||
func (r *queryResolver) GlobalSearch(ctx context.Context, query string) (*gql_generated.GlobalSearchResult, error) {
|
||||
query = cleanQuerry(query)
|
||||
defaultStore := r.storeController.DefaultStore
|
||||
olu := common.NewBaseOciLayoutUtils(r.storeController, r.log)
|
||||
|
||||
var name, tag string
|
||||
|
||||
_, err := fmt.Sscanf(query, "%s %s", &name, &tag)
|
||||
if err != nil {
|
||||
name = query
|
||||
}
|
||||
|
||||
repoList, err := defaultStore.GetRepositories()
|
||||
if err != nil {
|
||||
r.log.Error().Err(err).Msg("unable to search repositories")
|
||||
|
||||
return &gql_generated.GlobalSearchResult{}, err
|
||||
}
|
||||
|
||||
repos, images, layers := globalSearch(repoList, name, tag, olu, r.log)
|
||||
|
||||
return &gql_generated.GlobalSearchResult{
|
||||
Images: images,
|
||||
Repos: repos,
|
||||
Layers: layers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Query returns gql_generated.QueryResolver implementation.
|
||||
func (r *Resolver) Query() gql_generated.QueryResolver { return &queryResolver{r} }
|
||||
|
||||
|
|
|
@ -101,8 +101,8 @@ func createObjectsStore(rootDir string, cacheDir string, dedupe bool) (
|
|||
}
|
||||
|
||||
type FileInfoMock struct {
|
||||
isDirFn func() bool
|
||||
sizeFn func() int64
|
||||
IsDirFn func() bool
|
||||
SizeFn func() int64
|
||||
}
|
||||
|
||||
func (f *FileInfoMock) Path() string {
|
||||
|
@ -110,8 +110,8 @@ func (f *FileInfoMock) Path() string {
|
|||
}
|
||||
|
||||
func (f *FileInfoMock) Size() int64 {
|
||||
if f != nil && f.sizeFn != nil {
|
||||
return f.sizeFn()
|
||||
if f != nil && f.SizeFn != nil {
|
||||
return f.SizeFn()
|
||||
}
|
||||
|
||||
return int64(fileInfoSize)
|
||||
|
@ -122,18 +122,18 @@ func (f *FileInfoMock) ModTime() time.Time {
|
|||
}
|
||||
|
||||
func (f *FileInfoMock) IsDir() bool {
|
||||
if f != nil && f.isDirFn != nil {
|
||||
return f.isDirFn()
|
||||
if f != nil && f.IsDirFn != nil {
|
||||
return f.IsDirFn()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type FileWriterMock struct {
|
||||
writeFn func([]byte) (int, error)
|
||||
cancelFn func() error
|
||||
commitFn func() error
|
||||
closeFn func() error
|
||||
WriteFn func([]byte) (int, error)
|
||||
CancelFn func() error
|
||||
CommitFn func() error
|
||||
CloseFn func() error
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Size() int64 {
|
||||
|
@ -141,117 +141,117 @@ func (f *FileWriterMock) Size() int64 {
|
|||
}
|
||||
|
||||
func (f *FileWriterMock) Cancel() error {
|
||||
if f != nil && f.cancelFn != nil {
|
||||
return f.cancelFn()
|
||||
if f != nil && f.CancelFn != nil {
|
||||
return f.CancelFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Commit() error {
|
||||
if f != nil && f.commitFn != nil {
|
||||
return f.commitFn()
|
||||
if f != nil && f.CommitFn != nil {
|
||||
return f.CommitFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Write(p []byte) (int, error) {
|
||||
if f != nil && f.writeFn != nil {
|
||||
return f.writeFn(p)
|
||||
if f != nil && f.WriteFn != nil {
|
||||
return f.WriteFn(p)
|
||||
}
|
||||
|
||||
return 10, nil
|
||||
}
|
||||
|
||||
func (f *FileWriterMock) Close() error {
|
||||
if f != nil && f.closeFn != nil {
|
||||
return f.closeFn()
|
||||
if f != nil && f.CloseFn != nil {
|
||||
return f.CloseFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type StorageDriverMock struct {
|
||||
nameFn func() string
|
||||
getContentFn func(ctx context.Context, path string) ([]byte, error)
|
||||
putContentFn func(ctx context.Context, path string, content []byte) error
|
||||
readerFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error)
|
||||
writerFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error)
|
||||
statFn func(ctx context.Context, path string) (driver.FileInfo, error)
|
||||
listFn func(ctx context.Context, path string) ([]string, error)
|
||||
moveFn func(ctx context.Context, sourcePath, destPath string) error
|
||||
deleteFn func(ctx context.Context, path string) error
|
||||
walkFn func(ctx context.Context, path string, f driver.WalkFn) error
|
||||
NameFn func() string
|
||||
GetContentFn func(ctx context.Context, path string) ([]byte, error)
|
||||
PutContentFn func(ctx context.Context, path string, content []byte) error
|
||||
ReaderFn func(ctx context.Context, path string, offset int64) (io.ReadCloser, error)
|
||||
WriterFn func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error)
|
||||
StatFn func(ctx context.Context, path string) (driver.FileInfo, error)
|
||||
ListFn func(ctx context.Context, path string) ([]string, error)
|
||||
MoveFn func(ctx context.Context, sourcePath, destPath string) error
|
||||
DeleteFn func(ctx context.Context, path string) error
|
||||
WalkFn func(ctx context.Context, path string, f driver.WalkFn) error
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Name() string {
|
||||
if s != nil && s.nameFn != nil {
|
||||
return s.nameFn()
|
||||
if s != nil && s.NameFn != nil {
|
||||
return s.NameFn()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) GetContent(ctx context.Context, path string) ([]byte, error) {
|
||||
if s != nil && s.getContentFn != nil {
|
||||
return s.getContentFn(ctx, path)
|
||||
if s != nil && s.GetContentFn != nil {
|
||||
return s.GetContentFn(ctx, path)
|
||||
}
|
||||
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) PutContent(ctx context.Context, path string, content []byte) error {
|
||||
if s != nil && s.putContentFn != nil {
|
||||
return s.putContentFn(ctx, path, content)
|
||||
if s != nil && s.PutContentFn != nil {
|
||||
return s.PutContentFn(ctx, path, content)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
if s != nil && s.readerFn != nil {
|
||||
return s.readerFn(ctx, path, offset)
|
||||
if s != nil && s.ReaderFn != nil {
|
||||
return s.ReaderFn(ctx, path, offset)
|
||||
}
|
||||
|
||||
return ioutil.NopCloser(strings.NewReader("")), nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Writer(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
if s != nil && s.writerFn != nil {
|
||||
return s.writerFn(ctx, path, isAppend)
|
||||
if s != nil && s.WriterFn != nil {
|
||||
return s.WriterFn(ctx, path, isAppend)
|
||||
}
|
||||
|
||||
return &FileWriterMock{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Stat(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
if s != nil && s.statFn != nil {
|
||||
return s.statFn(ctx, path)
|
||||
if s != nil && s.StatFn != nil {
|
||||
return s.StatFn(ctx, path)
|
||||
}
|
||||
|
||||
return &FileInfoMock{}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) List(ctx context.Context, path string) ([]string, error) {
|
||||
if s != nil && s.listFn != nil {
|
||||
return s.listFn(ctx, path)
|
||||
if s != nil && s.ListFn != nil {
|
||||
return s.ListFn(ctx, path)
|
||||
}
|
||||
|
||||
return []string{"a"}, nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Move(ctx context.Context, sourcePath, destPath string) error {
|
||||
if s != nil && s.moveFn != nil {
|
||||
return s.moveFn(ctx, sourcePath, destPath)
|
||||
if s != nil && s.MoveFn != nil {
|
||||
return s.MoveFn(ctx, sourcePath, destPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StorageDriverMock) Delete(ctx context.Context, path string) error {
|
||||
if s != nil && s.deleteFn != nil {
|
||||
return s.deleteFn(ctx, path)
|
||||
if s != nil && s.DeleteFn != nil {
|
||||
return s.DeleteFn(ctx, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -262,8 +262,8 @@ func (s *StorageDriverMock) URLFor(ctx context.Context, path string, options map
|
|||
}
|
||||
|
||||
func (s *StorageDriverMock) Walk(ctx context.Context, path string, f driver.WalkFn) error {
|
||||
if s != nil && s.walkFn != nil {
|
||||
return s.walkFn(ctx, path, f)
|
||||
if s != nil && s.WalkFn != nil {
|
||||
return s.WalkFn(ctx, path, f)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -458,31 +458,31 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test storage driver errors", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{testImage}, errS3
|
||||
},
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
PutContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), errS3
|
||||
},
|
||||
walkFn: func(ctx context.Context, path string, f driver.WalkFn) error {
|
||||
WalkFn: func(ctx context.Context, path string, f driver.WalkFn) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, errS3
|
||||
},
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
DeleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -532,7 +532,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
tdir := t.TempDir()
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{testImage, testImage}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -541,10 +541,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
So(err, ShouldNotBeNil)
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{testImage, testImage}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return nil, errS3
|
||||
},
|
||||
})
|
||||
|
@ -555,10 +555,10 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test ValidateRepo2", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
})
|
||||
|
@ -568,13 +568,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test ValidateRepo3", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -585,13 +585,13 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
Convey("Test ValidateRepo4", t, func(c C) {
|
||||
ociLayout := []byte(`{"imageLayoutVersion": "9.9.9"}`)
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
listFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
ListFn: func(ctx context.Context, path string) ([]string, error) {
|
||||
return []string{"test/test/oci-layout", "test/test/index.json"}, nil
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, nil
|
||||
},
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return ociLayout, nil
|
||||
},
|
||||
})
|
||||
|
@ -601,7 +601,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test GetRepositories", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
walkFn: func(ctx context.Context, path string, f driver.WalkFn) error {
|
||||
WalkFn: func(ctx context.Context, path string, f driver.WalkFn) error {
|
||||
return f(new(FileInfoMock))
|
||||
},
|
||||
})
|
||||
|
@ -612,7 +612,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test DeleteImageManifest", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
getContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
GetContentFn: func(ctx context.Context, path string) ([]byte, error) {
|
||||
return []byte{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -628,7 +628,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test NewBlobUpload", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
PutContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -638,7 +638,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test GetBlobUpload", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -648,7 +648,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test PutBlobChunkStreamed", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -658,8 +658,8 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test PutBlobChunkStreamed2", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{writeFn: func(b []byte) (int, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{WriteFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
}}, nil
|
||||
},
|
||||
|
@ -670,7 +670,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test PutBlobChunk", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -680,12 +680,12 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test PutBlobChunk2", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
writeFn: func(b []byte) (int, error) {
|
||||
WriteFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
},
|
||||
cancelFn: func() error {
|
||||
CancelFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
|
@ -697,9 +697,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test PutBlobChunk3", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
writeFn: func(b []byte) (int, error) {
|
||||
WriteFn: func(b []byte) (int, error) {
|
||||
return 0, errS3
|
||||
},
|
||||
}, nil
|
||||
|
@ -711,9 +711,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FinishBlobUpload", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
commitFn: func() error {
|
||||
CommitFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
|
@ -726,9 +726,9 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FinishBlobUpload2", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{
|
||||
closeFn: func() error {
|
||||
CloseFn: func() error {
|
||||
return errS3
|
||||
},
|
||||
}, nil
|
||||
|
@ -741,7 +741,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FinishBlobUpload3", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return nil, errS3
|
||||
},
|
||||
})
|
||||
|
@ -752,7 +752,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FinishBlobUpload4", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -763,7 +763,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FullBlobUpload", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -781,7 +781,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test FullBlobUpload3", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -792,7 +792,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test GetBlob", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(strings.NewReader("")), errS3
|
||||
},
|
||||
})
|
||||
|
@ -803,7 +803,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test DeleteBlob", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
DeleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -814,7 +814,7 @@ func TestNegativeCasesObjectsStorage(t *testing.T) {
|
|||
|
||||
Convey("Test GetReferrers", t, func(c C) {
|
||||
imgStore = createMockStorage(testDir, tdir, false, &StorageDriverMock{
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
DeleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -1128,10 +1128,10 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
So(err, ShouldNotBeNil)
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath string, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath string, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return driver.FileInfoInternal{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -1148,7 +1148,7 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test DedupeBlob - error on second store.Stat()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
if path == "dst2" {
|
||||
return driver.FileInfoInternal{}, errS3
|
||||
}
|
||||
|
@ -1168,10 +1168,10 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test DedupeBlob - error on store.PutContent()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
PutContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return nil, nil
|
||||
},
|
||||
})
|
||||
|
@ -1187,10 +1187,10 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test DedupeBlob - error on store.Delete()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
deleteFn: func(ctx context.Context, path string) error {
|
||||
DeleteFn: func(ctx context.Context, path string) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return nil, nil
|
||||
},
|
||||
})
|
||||
|
@ -1206,13 +1206,13 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test copyBlob() - error on initRepo()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
PutContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return driver.FileInfoInternal{}, errS3
|
||||
},
|
||||
writerFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
WriterFn: func(ctx context.Context, path string, isAppend bool) (driver.FileWriter, error) {
|
||||
return &FileWriterMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -1230,10 +1230,10 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test copyBlob() - error on store.PutContent()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
putContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
PutContentFn: func(ctx context.Context, path string, content []byte) error {
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return driver.FileInfoInternal{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -1251,7 +1251,7 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test copyBlob() - error on store.Stat()", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return driver.FileInfoInternal{}, errS3
|
||||
},
|
||||
})
|
||||
|
@ -1290,7 +1290,7 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
if strings.Contains(path, "repo1/dst1") {
|
||||
return driver.FileInfoInternal{}, driver.PathNotFoundError{}
|
||||
}
|
||||
|
@ -1327,14 +1327,14 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{
|
||||
sizeFn: func() int64 {
|
||||
SizeFn: func() int64 {
|
||||
return 0
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
readerFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
ReaderFn: func(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
|
||||
if strings.Contains(path, "repo1/dst1") {
|
||||
return ioutil.NopCloser(strings.NewReader("")), errS3
|
||||
}
|
||||
|
@ -1356,14 +1356,14 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
blobPath := path.Join(testDir, "repo/blobs/sha256", hash)
|
||||
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
if destPath == blobPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errS3
|
||||
},
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
if path != blobPath {
|
||||
return nil, errS3
|
||||
}
|
||||
|
@ -1385,7 +1385,7 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test FullBlobUpload", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -1397,7 +1397,7 @@ func TestS3DedupeErr(t *testing.T) {
|
|||
Convey("Test FinishBlobUpload", t, func(c C) {
|
||||
tdir := t.TempDir()
|
||||
imgStore = createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
moveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
MoveFn: func(ctx context.Context, sourcePath, destPath string) error {
|
||||
return errS3
|
||||
},
|
||||
})
|
||||
|
@ -1419,7 +1419,7 @@ func TestInjectDedupe(t *testing.T) {
|
|||
|
||||
Convey("Inject errors in DedupeBlob function", t, func() {
|
||||
imgStore := createMockStorage(testDir, tdir, true, &StorageDriverMock{
|
||||
statFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
StatFn: func(ctx context.Context, path string) (driver.FileInfo, error) {
|
||||
return &FileInfoMock{}, errS3
|
||||
},
|
||||
})
|
||||
|
|
289
pkg/test/mocks/image_store_mock.go
Normal file
289
pkg/test/mocks/image_store_mock.go
Normal 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
121
pkg/test/mocks/oci_mock.go
Normal 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
|
||||
}
|
186
pkg/test/mocks/storage_driver_mock.go
Normal file
186
pkg/test/mocks/storage_driver_mock.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue