github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/driver/inmemory/driver.go (about)

     1  package inmemory
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/docker/distribution/context"
    12  	storagedriver "github.com/docker/distribution/registry/storage/driver"
    13  	"github.com/docker/distribution/registry/storage/driver/base"
    14  	"github.com/docker/distribution/registry/storage/driver/factory"
    15  )
    16  
    17  const driverName = "inmemory"
    18  
    19  func init() {
    20  	factory.Register(driverName, &inMemoryDriverFactory{})
    21  }
    22  
    23  // inMemoryDriverFacotry implements the factory.StorageDriverFactory interface.
    24  type inMemoryDriverFactory struct{}
    25  
    26  func (factory *inMemoryDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
    27  	return New(), nil
    28  }
    29  
    30  type driver struct {
    31  	root  *dir
    32  	mutex sync.RWMutex
    33  }
    34  
    35  // baseEmbed allows us to hide the Base embed.
    36  type baseEmbed struct {
    37  	base.Base
    38  }
    39  
    40  // Driver is a storagedriver.StorageDriver implementation backed by a local map.
    41  // Intended solely for example and testing purposes.
    42  type Driver struct {
    43  	baseEmbed // embedded, hidden base driver.
    44  }
    45  
    46  var _ storagedriver.StorageDriver = &Driver{}
    47  
    48  // New constructs a new Driver.
    49  func New() *Driver {
    50  	return &Driver{
    51  		baseEmbed: baseEmbed{
    52  			Base: base.Base{
    53  				StorageDriver: &driver{
    54  					root: &dir{
    55  						common: common{
    56  							p:   "/",
    57  							mod: time.Now(),
    58  						},
    59  					},
    60  				},
    61  			},
    62  		},
    63  	}
    64  }
    65  
    66  // Implement the storagedriver.StorageDriver interface.
    67  
    68  func (d *driver) Name() string {
    69  	return driverName
    70  }
    71  
    72  // GetContent retrieves the content stored at "path" as a []byte.
    73  func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) {
    74  	d.mutex.RLock()
    75  	defer d.mutex.RUnlock()
    76  
    77  	rc, err := d.ReadStream(ctx, path, 0)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  	defer rc.Close()
    82  
    83  	return ioutil.ReadAll(rc)
    84  }
    85  
    86  // PutContent stores the []byte content at a location designated by "path".
    87  func (d *driver) PutContent(ctx context.Context, p string, contents []byte) error {
    88  	d.mutex.Lock()
    89  	defer d.mutex.Unlock()
    90  
    91  	f, err := d.root.mkfile(p)
    92  	if err != nil {
    93  		// TODO(stevvooe): Again, we need to clarify when this is not a
    94  		// directory in StorageDriver API.
    95  		return fmt.Errorf("not a file")
    96  	}
    97  
    98  	f.truncate()
    99  	f.WriteAt(contents, 0)
   100  
   101  	return nil
   102  }
   103  
   104  // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
   105  // given byte offset.
   106  func (d *driver) ReadStream(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
   107  	d.mutex.RLock()
   108  	defer d.mutex.RUnlock()
   109  
   110  	if offset < 0 {
   111  		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
   112  	}
   113  
   114  	path = normalize(path)
   115  	found := d.root.find(path)
   116  
   117  	if found.path() != path {
   118  		return nil, storagedriver.PathNotFoundError{Path: path}
   119  	}
   120  
   121  	if found.isdir() {
   122  		return nil, fmt.Errorf("%q is a directory", path)
   123  	}
   124  
   125  	return ioutil.NopCloser(found.(*file).sectionReader(offset)), nil
   126  }
   127  
   128  // WriteStream stores the contents of the provided io.ReadCloser at a location
   129  // designated by the given path.
   130  func (d *driver) WriteStream(ctx context.Context, path string, offset int64, reader io.Reader) (nn int64, err error) {
   131  	d.mutex.Lock()
   132  	defer d.mutex.Unlock()
   133  
   134  	if offset < 0 {
   135  		return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
   136  	}
   137  
   138  	normalized := normalize(path)
   139  
   140  	f, err := d.root.mkfile(normalized)
   141  	if err != nil {
   142  		return 0, fmt.Errorf("not a file")
   143  	}
   144  
   145  	// Unlock while we are reading from the source, in case we are reading
   146  	// from the same mfs instance. This can be fixed by a more granular
   147  	// locking model.
   148  	d.mutex.Unlock()
   149  	d.mutex.RLock() // Take the readlock to block other writers.
   150  	var buf bytes.Buffer
   151  
   152  	nn, err = buf.ReadFrom(reader)
   153  	if err != nil {
   154  		// TODO(stevvooe): This condition is odd and we may need to clarify:
   155  		// we've read nn bytes from reader but have written nothing to the
   156  		// backend. What is the correct return value? Really, the caller needs
   157  		// to know that the reader has been advanced and reattempting the
   158  		// operation is incorrect.
   159  		d.mutex.RUnlock()
   160  		d.mutex.Lock()
   161  		return nn, err
   162  	}
   163  
   164  	d.mutex.RUnlock()
   165  	d.mutex.Lock()
   166  	f.WriteAt(buf.Bytes(), offset)
   167  	return nn, err
   168  }
   169  
   170  // Stat returns info about the provided path.
   171  func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) {
   172  	d.mutex.RLock()
   173  	defer d.mutex.RUnlock()
   174  
   175  	normalized := normalize(path)
   176  	found := d.root.find(path)
   177  
   178  	if found.path() != normalized {
   179  		return nil, storagedriver.PathNotFoundError{Path: path}
   180  	}
   181  
   182  	fi := storagedriver.FileInfoFields{
   183  		Path:    path,
   184  		IsDir:   found.isdir(),
   185  		ModTime: found.modtime(),
   186  	}
   187  
   188  	if !fi.IsDir {
   189  		fi.Size = int64(len(found.(*file).data))
   190  	}
   191  
   192  	return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
   193  }
   194  
   195  // List returns a list of the objects that are direct descendants of the given
   196  // path.
   197  func (d *driver) List(ctx context.Context, path string) ([]string, error) {
   198  	d.mutex.RLock()
   199  	defer d.mutex.RUnlock()
   200  
   201  	normalized := normalize(path)
   202  
   203  	found := d.root.find(normalized)
   204  
   205  	if !found.isdir() {
   206  		return nil, fmt.Errorf("not a directory") // TODO(stevvooe): Need error type for this...
   207  	}
   208  
   209  	entries, err := found.(*dir).list(normalized)
   210  
   211  	if err != nil {
   212  		switch err {
   213  		case errNotExists:
   214  			return nil, storagedriver.PathNotFoundError{Path: path}
   215  		case errIsNotDir:
   216  			return nil, fmt.Errorf("not a directory")
   217  		default:
   218  			return nil, err
   219  		}
   220  	}
   221  
   222  	return entries, nil
   223  }
   224  
   225  // Move moves an object stored at sourcePath to destPath, removing the original
   226  // object.
   227  func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error {
   228  	d.mutex.Lock()
   229  	defer d.mutex.Unlock()
   230  
   231  	normalizedSrc, normalizedDst := normalize(sourcePath), normalize(destPath)
   232  
   233  	err := d.root.move(normalizedSrc, normalizedDst)
   234  	switch err {
   235  	case errNotExists:
   236  		return storagedriver.PathNotFoundError{Path: destPath}
   237  	default:
   238  		return err
   239  	}
   240  }
   241  
   242  // Delete recursively deletes all objects stored at "path" and its subpaths.
   243  func (d *driver) Delete(ctx context.Context, path string) error {
   244  	d.mutex.Lock()
   245  	defer d.mutex.Unlock()
   246  
   247  	normalized := normalize(path)
   248  
   249  	err := d.root.delete(normalized)
   250  	switch err {
   251  	case errNotExists:
   252  		return storagedriver.PathNotFoundError{Path: path}
   253  	default:
   254  		return err
   255  	}
   256  }
   257  
   258  // URLFor returns a URL which may be used to retrieve the content stored at the given path.
   259  // May return an UnsupportedMethodErr in certain StorageDriver implementations.
   260  func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
   261  	return "", storagedriver.ErrUnsupportedMethod{}
   262  }