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