...

Source file src/github.com/docker/distribution/registry/storage/catalog_test.go

Documentation: github.com/docker/distribution/registry/storage

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"math/rand"
     8  	"testing"
     9  
    10  	"github.com/distribution/reference"
    11  	"github.com/docker/distribution"
    12  	"github.com/docker/distribution/registry/storage/cache/memory"
    13  	"github.com/docker/distribution/registry/storage/driver"
    14  	"github.com/docker/distribution/registry/storage/driver/inmemory"
    15  	"github.com/docker/distribution/testutil"
    16  	"github.com/opencontainers/go-digest"
    17  )
    18  
    19  type setupEnv struct {
    20  	ctx      context.Context
    21  	driver   driver.StorageDriver
    22  	expected []string
    23  	registry distribution.Namespace
    24  }
    25  
    26  func setupFS(t *testing.T) *setupEnv {
    27  	d := inmemory.New()
    28  	ctx := context.Background()
    29  	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect, EnableSchema1)
    30  	if err != nil {
    31  		t.Fatalf("error creating registry: %v", err)
    32  	}
    33  
    34  	repos := []string{
    35  		"foo/a",
    36  		"foo/b",
    37  		"foo-bar/a",
    38  		"bar/c",
    39  		"bar/d",
    40  		"bar/e",
    41  		"foo/d/in",
    42  		"foo-bar/b",
    43  		"test",
    44  	}
    45  
    46  	for _, repo := range repos {
    47  		makeRepo(ctx, t, repo, registry)
    48  	}
    49  
    50  	expected := []string{
    51  		"bar/c",
    52  		"bar/d",
    53  		"bar/e",
    54  		"foo/a",
    55  		"foo/b",
    56  		"foo/d/in",
    57  		"foo-bar/a",
    58  		"foo-bar/b",
    59  		"test",
    60  	}
    61  
    62  	return &setupEnv{
    63  		ctx:      ctx,
    64  		driver:   d,
    65  		expected: expected,
    66  		registry: registry,
    67  	}
    68  }
    69  
    70  func makeRepo(ctx context.Context, t *testing.T, name string, reg distribution.Namespace) {
    71  	named, err := reference.WithName(name)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	repo, _ := reg.Repository(ctx, named)
    77  	manifests, _ := repo.Manifests(ctx)
    78  
    79  	layers, err := testutil.CreateRandomLayers(1)
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  
    84  	err = testutil.UploadBlobs(repo, layers)
    85  	if err != nil {
    86  		t.Fatalf("failed to upload layers: %v", err)
    87  	}
    88  
    89  	getKeys := func(digests map[digest.Digest]io.ReadSeeker) (ds []digest.Digest) {
    90  		for d := range digests {
    91  			ds = append(ds, d)
    92  		}
    93  		return
    94  	}
    95  
    96  	manifest, err := testutil.MakeSchema1Manifest(getKeys(layers))
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  
   101  	_, err = manifests.Put(ctx, manifest)
   102  	if err != nil {
   103  		t.Fatalf("manifest upload failed: %v", err)
   104  	}
   105  
   106  }
   107  
   108  func TestCatalog(t *testing.T) {
   109  	env := setupFS(t)
   110  
   111  	p := make([]string, 50)
   112  
   113  	numFilled, err := env.registry.Repositories(env.ctx, p, "")
   114  	if numFilled != len(env.expected) {
   115  		t.Errorf("missing items in catalog")
   116  	}
   117  
   118  	if !testEq(p, env.expected, len(env.expected)) {
   119  		t.Errorf("Expected catalog repos err")
   120  	}
   121  
   122  	if err != io.EOF {
   123  		t.Errorf("Catalog has more values which we aren't expecting")
   124  	}
   125  }
   126  
   127  func TestCatalogInParts(t *testing.T) {
   128  	env := setupFS(t)
   129  
   130  	chunkLen := 3
   131  	p := make([]string, chunkLen)
   132  
   133  	numFilled, err := env.registry.Repositories(env.ctx, p, "")
   134  	if err == io.EOF || numFilled != len(p) {
   135  		t.Errorf("Expected more values in catalog")
   136  	}
   137  
   138  	if !testEq(p, env.expected[0:chunkLen], numFilled) {
   139  		t.Errorf("Expected catalog first chunk err")
   140  	}
   141  
   142  	lastRepo := p[len(p)-1]
   143  	numFilled, err = env.registry.Repositories(env.ctx, p, lastRepo)
   144  
   145  	if err == io.EOF || numFilled != len(p) {
   146  		t.Errorf("Expected more values in catalog")
   147  	}
   148  
   149  	if !testEq(p, env.expected[chunkLen:chunkLen*2], numFilled) {
   150  		t.Errorf("Expected catalog second chunk err")
   151  	}
   152  
   153  	lastRepo = p[len(p)-1]
   154  	numFilled, err = env.registry.Repositories(env.ctx, p, lastRepo)
   155  
   156  	if err != io.EOF || numFilled != len(p) {
   157  		t.Errorf("Expected end of catalog")
   158  	}
   159  
   160  	if !testEq(p, env.expected[chunkLen*2:chunkLen*3], numFilled) {
   161  		t.Errorf("Expected catalog third chunk err")
   162  	}
   163  
   164  	lastRepo = p[len(p)-1]
   165  	numFilled, err = env.registry.Repositories(env.ctx, p, lastRepo)
   166  
   167  	if err != io.EOF {
   168  		t.Errorf("Catalog has more values which we aren't expecting")
   169  	}
   170  
   171  	if numFilled != 0 {
   172  		t.Errorf("Expected catalog fourth chunk err")
   173  	}
   174  }
   175  
   176  func TestCatalogEnumerate(t *testing.T) {
   177  	env := setupFS(t)
   178  
   179  	var repos []string
   180  	repositoryEnumerator := env.registry.(distribution.RepositoryEnumerator)
   181  	err := repositoryEnumerator.Enumerate(env.ctx, func(repoName string) error {
   182  		repos = append(repos, repoName)
   183  		return nil
   184  	})
   185  	if err != nil {
   186  		t.Errorf("Expected catalog enumerate err")
   187  	}
   188  
   189  	if len(repos) != len(env.expected) {
   190  		t.Errorf("Expected catalog enumerate doesn't have correct number of values")
   191  	}
   192  
   193  	if !testEq(repos, env.expected, len(env.expected)) {
   194  		t.Errorf("Expected catalog enumerate not over all values")
   195  	}
   196  }
   197  
   198  func testEq(a, b []string, size int) bool {
   199  	for cnt := 0; cnt < size-1; cnt++ {
   200  		if a[cnt] != b[cnt] {
   201  			return false
   202  		}
   203  	}
   204  	return true
   205  }
   206  
   207  func setupBadWalkEnv(t *testing.T) *setupEnv {
   208  	d := newBadListDriver()
   209  	ctx := context.Background()
   210  	registry, err := NewRegistry(ctx, d, BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider()), EnableRedirect, EnableSchema1)
   211  	if err != nil {
   212  		t.Fatalf("error creating registry: %v", err)
   213  	}
   214  
   215  	return &setupEnv{
   216  		ctx:      ctx,
   217  		driver:   d,
   218  		registry: registry,
   219  	}
   220  }
   221  
   222  type badListDriver struct {
   223  	driver.StorageDriver
   224  }
   225  
   226  var _ driver.StorageDriver = &badListDriver{}
   227  
   228  func newBadListDriver() *badListDriver {
   229  	return &badListDriver{StorageDriver: inmemory.New()}
   230  }
   231  
   232  func (d *badListDriver) List(ctx context.Context, path string) ([]string, error) {
   233  	return nil, fmt.Errorf("List error")
   234  }
   235  
   236  func TestCatalogWalkError(t *testing.T) {
   237  	env := setupBadWalkEnv(t)
   238  	p := make([]string, 1)
   239  
   240  	_, err := env.registry.Repositories(env.ctx, p, "")
   241  	if err == io.EOF {
   242  		t.Errorf("Expected catalog driver list error")
   243  	}
   244  }
   245  
   246  func BenchmarkPathCompareEqual(B *testing.B) {
   247  	B.StopTimer()
   248  	pp := randomPath(100)
   249  	// make a real copy
   250  	ppb := append([]byte{}, []byte(pp)...)
   251  	a, b := pp, string(ppb)
   252  
   253  	B.StartTimer()
   254  	for i := 0; i < B.N; i++ {
   255  		lessPath(a, b)
   256  	}
   257  }
   258  
   259  func BenchmarkPathCompareNotEqual(B *testing.B) {
   260  	B.StopTimer()
   261  	a, b := randomPath(100), randomPath(100)
   262  	B.StartTimer()
   263  
   264  	for i := 0; i < B.N; i++ {
   265  		lessPath(a, b)
   266  	}
   267  }
   268  
   269  func BenchmarkPathCompareNative(B *testing.B) {
   270  	B.StopTimer()
   271  	a, b := randomPath(100), randomPath(100)
   272  	B.StartTimer()
   273  
   274  	for i := 0; i < B.N; i++ {
   275  		c := a < b
   276  		_ = c && false
   277  	}
   278  }
   279  
   280  func BenchmarkPathCompareNativeEqual(B *testing.B) {
   281  	B.StopTimer()
   282  	pp := randomPath(100)
   283  	a, b := pp, pp
   284  	B.StartTimer()
   285  
   286  	for i := 0; i < B.N; i++ {
   287  		c := a < b
   288  		_ = c && false
   289  	}
   290  }
   291  
   292  var filenameChars = []byte("abcdefghijklmnopqrstuvwxyz0123456789")
   293  var separatorChars = []byte("._-")
   294  
   295  func randomPath(length int64) string {
   296  	path := "/"
   297  	for int64(len(path)) < length {
   298  		chunkLength := rand.Int63n(length-int64(len(path))) + 1
   299  		chunk := randomFilename(chunkLength)
   300  		path += chunk
   301  		remaining := length - int64(len(path))
   302  		if remaining == 1 {
   303  			path += randomFilename(1)
   304  		} else if remaining > 1 {
   305  			path += "/"
   306  		}
   307  	}
   308  	return path
   309  }
   310  
   311  func randomFilename(length int64) string {
   312  	b := make([]byte, length)
   313  	wasSeparator := true
   314  	for i := range b {
   315  		if !wasSeparator && i < len(b)-1 && rand.Intn(4) == 0 {
   316  			b[i] = separatorChars[rand.Intn(len(separatorChars))]
   317  			wasSeparator = true
   318  		} else {
   319  			b[i] = filenameChars[rand.Intn(len(filenameChars))]
   320  			wasSeparator = false
   321  		}
   322  	}
   323  	return string(b)
   324  }
   325  

View as plain text