2015-07-13 20:08:13 +00:00
package storage
import (
2017-08-11 22:31:16 +00:00
"context"
2016-07-13 23:41:51 +00:00
"fmt"
2015-07-17 18:42:47 +00:00
"io"
2023-08-18 12:52:37 +00:00
"math/rand"
2015-07-13 20:08:13 +00:00
"testing"
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/reference"
"github.com/distribution/distribution/v3/registry/storage/cache/memory"
"github.com/distribution/distribution/v3/registry/storage/driver"
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
"github.com/distribution/distribution/v3/testutil"
2016-12-17 00:28:34 +00:00
"github.com/opencontainers/go-digest"
2015-07-13 20:08:13 +00:00
)
type setupEnv struct {
ctx context . Context
driver driver . StorageDriver
expected [ ] string
registry distribution . Namespace
}
func setupFS ( t * testing . T ) * setupEnv {
d := inmemory . New ( )
ctx := context . Background ( )
2022-07-13 00:42:48 +00:00
registry , err := NewRegistry ( ctx , d , BlobDescriptorCacheProvider ( memory . NewInMemoryBlobDescriptorCacheProvider ( memory . UnlimitedSize ) ) , EnableRedirect , EnableSchema1 )
2015-08-18 17:56:27 +00:00
if err != nil {
t . Fatalf ( "error creating registry: %v" , err )
}
2015-07-13 20:08:13 +00:00
repos := [ ] string {
2016-07-13 23:39:34 +00:00
"foo/a" ,
"foo/b" ,
2016-07-21 14:37:51 +00:00
"foo-bar/a" ,
2016-07-13 23:39:34 +00:00
"bar/c" ,
"bar/d" ,
2016-07-21 14:37:51 +00:00
"bar/e" ,
2016-07-13 23:39:34 +00:00
"foo/d/in" ,
2016-07-21 14:37:51 +00:00
"foo-bar/b" ,
"test" ,
2015-07-13 20:08:13 +00:00
}
for _ , repo := range repos {
2016-10-15 00:03:08 +00:00
makeRepo ( ctx , t , repo , registry )
2015-07-13 20:08:13 +00:00
}
expected := [ ] string {
"bar/c" ,
"bar/d" ,
2016-07-21 14:37:51 +00:00
"bar/e" ,
2015-07-13 20:08:13 +00:00
"foo/a" ,
"foo/b" ,
"foo/d/in" ,
2016-07-21 14:37:51 +00:00
"foo-bar/a" ,
"foo-bar/b" ,
"test" ,
2015-07-13 20:08:13 +00:00
}
return & setupEnv {
ctx : ctx ,
driver : d ,
expected : expected ,
registry : registry ,
}
}
2016-10-15 00:03:08 +00:00
func makeRepo ( ctx context . Context , t * testing . T , name string , reg distribution . Namespace ) {
2017-01-14 01:06:03 +00:00
named , err := reference . WithName ( name )
2016-07-13 23:39:34 +00:00
if err != nil {
t . Fatal ( err )
}
repo , _ := reg . Repository ( ctx , named )
manifests , _ := repo . Manifests ( ctx )
layers , err := testutil . CreateRandomLayers ( 1 )
if err != nil {
t . Fatal ( err )
}
err = testutil . UploadBlobs ( repo , layers )
if err != nil {
t . Fatalf ( "failed to upload layers: %v" , err )
}
getKeys := func ( digests map [ digest . Digest ] io . ReadSeeker ) ( ds [ ] digest . Digest ) {
for d := range digests {
ds = append ( ds , d )
}
return
}
2023-05-09 11:18:47 +00:00
manifest , err := testutil . MakeSchema1Manifest ( getKeys ( layers ) ) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
2016-07-13 23:39:34 +00:00
if err != nil {
t . Fatal ( err )
}
_ , err = manifests . Put ( ctx , manifest )
if err != nil {
t . Fatalf ( "manifest upload failed: %v" , err )
}
}
2015-07-13 20:08:13 +00:00
func TestCatalog ( t * testing . T ) {
env := setupFS ( t )
2015-07-17 18:42:47 +00:00
p := make ( [ ] string , 50 )
numFilled , err := env . registry . Repositories ( env . ctx , p , "" )
2016-07-13 23:39:34 +00:00
if numFilled != len ( env . expected ) {
t . Errorf ( "missing items in catalog" )
}
2015-07-13 20:08:13 +00:00
2016-07-13 23:39:34 +00:00
if ! testEq ( p , env . expected , len ( env . expected ) ) {
2015-07-13 20:08:13 +00:00
t . Errorf ( "Expected catalog repos err" )
}
2015-07-17 18:42:47 +00:00
if err != io . EOF {
2015-07-13 20:08:13 +00:00
t . Errorf ( "Catalog has more values which we aren't expecting" )
}
}
func TestCatalogInParts ( t * testing . T ) {
env := setupFS ( t )
2016-07-21 14:37:51 +00:00
chunkLen := 3
2015-07-17 18:42:47 +00:00
p := make ( [ ] string , chunkLen )
2015-07-13 20:08:13 +00:00
2015-07-17 18:42:47 +00:00
numFilled , err := env . registry . Repositories ( env . ctx , p , "" )
if err == io . EOF || numFilled != len ( p ) {
t . Errorf ( "Expected more values in catalog" )
2015-07-13 20:08:13 +00:00
}
2015-07-17 18:42:47 +00:00
if ! testEq ( p , env . expected [ 0 : chunkLen ] , numFilled ) {
t . Errorf ( "Expected catalog first chunk err" )
2015-07-13 20:08:13 +00:00
}
2015-07-17 18:42:47 +00:00
lastRepo := p [ len ( p ) - 1 ]
numFilled , err = env . registry . Repositories ( env . ctx , p , lastRepo )
2015-07-13 20:08:13 +00:00
2015-07-17 18:42:47 +00:00
if err == io . EOF || numFilled != len ( p ) {
t . Errorf ( "Expected more values in catalog" )
2015-07-13 20:08:13 +00:00
}
2015-07-17 18:42:47 +00:00
if ! testEq ( p , env . expected [ chunkLen : chunkLen * 2 ] , numFilled ) {
t . Errorf ( "Expected catalog second chunk err" )
2015-07-13 20:08:13 +00:00
}
2015-07-17 18:42:47 +00:00
lastRepo = p [ len ( p ) - 1 ]
numFilled , err = env . registry . Repositories ( env . ctx , p , lastRepo )
2015-07-13 20:08:13 +00:00
2016-07-21 14:37:51 +00:00
if err != io . EOF || numFilled != len ( p ) {
t . Errorf ( "Expected end of catalog" )
}
if ! testEq ( p , env . expected [ chunkLen * 2 : chunkLen * 3 ] , numFilled ) {
t . Errorf ( "Expected catalog third chunk err" )
}
lastRepo = p [ len ( p ) - 1 ]
numFilled , err = env . registry . Repositories ( env . ctx , p , lastRepo )
2015-07-17 18:42:47 +00:00
if err != io . EOF {
t . Errorf ( "Catalog has more values which we aren't expecting" )
2015-07-13 20:08:13 +00:00
}
2016-07-21 14:37:51 +00:00
if numFilled != 0 {
t . Errorf ( "Expected catalog fourth chunk err" )
2015-07-13 20:08:13 +00:00
}
2016-08-10 00:42:26 +00:00
}
func TestCatalogEnumerate ( t * testing . T ) {
env := setupFS ( t )
2015-07-13 20:08:13 +00:00
2016-08-10 00:42:26 +00:00
var repos [ ] string
repositoryEnumerator := env . registry . ( distribution . RepositoryEnumerator )
err := repositoryEnumerator . Enumerate ( env . ctx , func ( repoName string ) error {
repos = append ( repos , repoName )
return nil
} )
if err != nil {
t . Errorf ( "Expected catalog enumerate err" )
}
if len ( repos ) != len ( env . expected ) {
t . Errorf ( "Expected catalog enumerate doesn't have correct number of values" )
}
if ! testEq ( repos , env . expected , len ( env . expected ) ) {
t . Errorf ( "Expected catalog enumerate not over all values" )
}
2015-07-13 20:08:13 +00:00
}
2015-07-17 18:42:47 +00:00
func testEq ( a , b [ ] string , size int ) bool {
for cnt := 0 ; cnt < size - 1 ; cnt ++ {
if a [ cnt ] != b [ cnt ] {
2015-07-13 20:08:13 +00:00
return false
}
}
return true
}
2016-07-13 23:41:51 +00:00
func setupBadWalkEnv ( t * testing . T ) * setupEnv {
d := newBadListDriver ( )
ctx := context . Background ( )
2022-07-13 00:42:48 +00:00
registry , err := NewRegistry ( ctx , d , BlobDescriptorCacheProvider ( memory . NewInMemoryBlobDescriptorCacheProvider ( memory . UnlimitedSize ) ) , EnableRedirect , EnableSchema1 )
2016-07-13 23:41:51 +00:00
if err != nil {
t . Fatalf ( "error creating registry: %v" , err )
}
return & setupEnv {
ctx : ctx ,
driver : d ,
registry : registry ,
}
}
type badListDriver struct {
driver . StorageDriver
}
var _ driver . StorageDriver = & badListDriver { }
func newBadListDriver ( ) * badListDriver {
return & badListDriver { StorageDriver : inmemory . New ( ) }
}
func ( d * badListDriver ) List ( ctx context . Context , path string ) ( [ ] string , error ) {
return nil , fmt . Errorf ( "List error" )
}
func TestCatalogWalkError ( t * testing . T ) {
env := setupBadWalkEnv ( t )
p := make ( [ ] string , 1 )
_ , err := env . registry . Repositories ( env . ctx , p , "" )
if err == io . EOF {
t . Errorf ( "Expected catalog driver list error" )
}
}
2023-08-18 12:52:28 +00:00
func BenchmarkPathCompareEqual ( B * testing . B ) {
B . StopTimer ( )
pp := randomPath ( 100 )
// make a real copy
ppb := append ( [ ] byte { } , [ ] byte ( pp ) ... )
a , b := pp , string ( ppb )
B . StartTimer ( )
for i := 0 ; i < B . N ; i ++ {
lessPath ( a , b )
}
}
func BenchmarkPathCompareNotEqual ( B * testing . B ) {
B . StopTimer ( )
a , b := randomPath ( 100 ) , randomPath ( 100 )
B . StartTimer ( )
for i := 0 ; i < B . N ; i ++ {
lessPath ( a , b )
}
}
func BenchmarkPathCompareNative ( B * testing . B ) {
B . StopTimer ( )
a , b := randomPath ( 100 ) , randomPath ( 100 )
B . StartTimer ( )
for i := 0 ; i < B . N ; i ++ {
c := a < b
_ = c && false
}
}
func BenchmarkPathCompareNativeEqual ( B * testing . B ) {
B . StopTimer ( )
pp := randomPath ( 100 )
a , b := pp , pp
B . StartTimer ( )
for i := 0 ; i < B . N ; i ++ {
c := a < b
_ = c && false
}
}
var (
filenameChars = [ ] byte ( "abcdefghijklmnopqrstuvwxyz0123456789" )
separatorChars = [ ] byte ( "._-" )
)
func randomPath ( length int64 ) string {
path := "/"
for int64 ( len ( path ) ) < length {
chunkLength := rand . Int63n ( length - int64 ( len ( path ) ) ) + 1
chunk := randomFilename ( chunkLength )
path += chunk
remaining := length - int64 ( len ( path ) )
if remaining == 1 {
path += randomFilename ( 1 )
} else if remaining > 1 {
path += "/"
}
}
return path
}
func randomFilename ( length int64 ) string {
b := make ( [ ] byte , length )
wasSeparator := true
for i := range b {
if ! wasSeparator && i < len ( b ) - 1 && rand . Intn ( 4 ) == 0 {
b [ i ] = separatorChars [ rand . Intn ( len ( separatorChars ) ) ]
wasSeparator = true
} else {
b [ i ] = filenameChars [ rand . Intn ( len ( filenameChars ) ) ]
wasSeparator = false
}
}
return string ( b )
}