...

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

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

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"io"
     7  	"path"
     8  	"strings"
     9  
    10  	"github.com/distribution/reference"
    11  	"github.com/docker/distribution/registry/storage/driver"
    12  )
    13  
    14  // Returns a list, or partial list, of repositories in the registry.
    15  // Because it's a quite expensive operation, it should only be used when building up
    16  // an initial set of repositories.
    17  func (reg *registry) Repositories(ctx context.Context, repos []string, last string) (n int, err error) {
    18  	var finishedWalk bool
    19  	var foundRepos []string
    20  
    21  	if len(repos) == 0 {
    22  		return 0, errors.New("no space in slice")
    23  	}
    24  
    25  	root, err := pathFor(repositoriesRootPathSpec{})
    26  	if err != nil {
    27  		return 0, err
    28  	}
    29  
    30  	err = reg.blobStore.driver.Walk(ctx, root, func(fileInfo driver.FileInfo) error {
    31  		err := handleRepository(fileInfo, root, last, func(repoPath string) error {
    32  			foundRepos = append(foundRepos, repoPath)
    33  			return nil
    34  		})
    35  		if err != nil {
    36  			return err
    37  		}
    38  
    39  		// if we've filled our array, no need to walk any further
    40  		if len(foundRepos) == len(repos) {
    41  			finishedWalk = true
    42  			return driver.ErrSkipDir
    43  		}
    44  
    45  		return nil
    46  	})
    47  
    48  	n = copy(repos, foundRepos)
    49  
    50  	if err != nil {
    51  		return n, err
    52  	} else if !finishedWalk {
    53  		// We didn't fill buffer. No more records are available.
    54  		return n, io.EOF
    55  	}
    56  
    57  	return n, err
    58  }
    59  
    60  // Enumerate applies ingester to each repository
    61  func (reg *registry) Enumerate(ctx context.Context, ingester func(string) error) error {
    62  	root, err := pathFor(repositoriesRootPathSpec{})
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	err = reg.blobStore.driver.Walk(ctx, root, func(fileInfo driver.FileInfo) error {
    68  		return handleRepository(fileInfo, root, "", ingester)
    69  	})
    70  
    71  	return err
    72  }
    73  
    74  // Remove removes a repository from storage
    75  func (reg *registry) Remove(ctx context.Context, name reference.Named) error {
    76  	root, err := pathFor(repositoriesRootPathSpec{})
    77  	if err != nil {
    78  		return err
    79  	}
    80  	repoDir := path.Join(root, name.Name())
    81  	return reg.driver.Delete(ctx, repoDir)
    82  }
    83  
    84  // lessPath returns true if one path a is less than path b.
    85  //
    86  // A component-wise comparison is done, rather than the lexical comparison of
    87  // strings.
    88  func lessPath(a, b string) bool {
    89  	// we provide this behavior by making separator always sort first.
    90  	return compareReplaceInline(a, b, '/', '\x00') < 0
    91  }
    92  
    93  // compareReplaceInline modifies runtime.cmpstring to replace old with new
    94  // during a byte-wise comparison.
    95  func compareReplaceInline(s1, s2 string, old, new byte) int {
    96  	// TODO(stevvooe): We are missing an optimization when the s1 and s2 have
    97  	// the exact same slice header. It will make the code unsafe but can
    98  	// provide some extra performance.
    99  
   100  	l := len(s1)
   101  	if len(s2) < l {
   102  		l = len(s2)
   103  	}
   104  
   105  	for i := 0; i < l; i++ {
   106  		c1, c2 := s1[i], s2[i]
   107  		if c1 == old {
   108  			c1 = new
   109  		}
   110  
   111  		if c2 == old {
   112  			c2 = new
   113  		}
   114  
   115  		if c1 < c2 {
   116  			return -1
   117  		}
   118  
   119  		if c1 > c2 {
   120  			return +1
   121  		}
   122  	}
   123  
   124  	if len(s1) < len(s2) {
   125  		return -1
   126  	}
   127  
   128  	if len(s1) > len(s2) {
   129  		return +1
   130  	}
   131  
   132  	return 0
   133  }
   134  
   135  // handleRepository calls function fn with a repository path if fileInfo
   136  // has a path of a repository under root and that it is lexographically
   137  // after last. Otherwise, it will return ErrSkipDir. This should be used
   138  // with Walk to do handling with repositories in a storage.
   139  func handleRepository(fileInfo driver.FileInfo, root, last string, fn func(repoPath string) error) error {
   140  	filePath := fileInfo.Path()
   141  
   142  	// lop the base path off
   143  	repo := filePath[len(root)+1:]
   144  
   145  	_, file := path.Split(repo)
   146  	if file == "_layers" {
   147  		repo = strings.TrimSuffix(repo, "/_layers")
   148  		if lessPath(last, repo) {
   149  			if err := fn(repo); err != nil {
   150  				return err
   151  			}
   152  		}
   153  		return driver.ErrSkipDir
   154  	} else if strings.HasPrefix(file, "_") {
   155  		return driver.ErrSkipDir
   156  	}
   157  
   158  	return nil
   159  }
   160  

View as plain text