...

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

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

     1  package inmemory
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"sync"
     9  	"time"
    10  
    11  	storagedriver "github.com/docker/distribution/registry/storage/driver"
    12  	"github.com/docker/distribution/registry/storage/driver/base"
    13  	"github.com/docker/distribution/registry/storage/driver/factory"
    14  )
    15  
    16  const driverName = "inmemory"
    17  
    18  func init() {
    19  	factory.Register(driverName, &inMemoryDriverFactory{})
    20  }
    21  
    22  // inMemoryDriverFacotry implements the factory.StorageDriverFactory interface.
    23  type inMemoryDriverFactory struct{}
    24  
    25  func (factory *inMemoryDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
    26  	return New(), nil
    27  }
    28  
    29  type driver struct {
    30  	root  *dir
    31  	mutex sync.RWMutex
    32  }
    33  
    34  // baseEmbed allows us to hide the Base embed.
    35  type baseEmbed struct {
    36  	base.Base
    37  }
    38  
    39  // Driver is a storagedriver.StorageDriver implementation backed by a local map.
    40  // Intended solely for example and testing purposes.
    41  type Driver struct {
    42  	baseEmbed // embedded, hidden base driver.
    43  }
    44  
    45  var _ storagedriver.StorageDriver = &Driver{}
    46  
    47  // New constructs a new Driver.
    48  func New() *Driver {
    49  	return &Driver{
    50  		baseEmbed: baseEmbed{
    51  			Base: base.Base{
    52  				StorageDriver: &driver{
    53  					root: &dir{
    54  						common: common{
    55  							p:   "/",
    56  							mod: time.Now(),
    57  						},
    58  					},
    59  				},
    60  			},
    61  		},
    62  	}
    63  }
    64  
    65  // Implement the storagedriver.StorageDriver interface.
    66  
    67  func (d *driver) Name() string {
    68  	return driverName
    69  }
    70  
    71  // GetContent retrieves the content stored at "path" as a []byte.
    72  func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) {
    73  	d.mutex.RLock()
    74  	defer d.mutex.RUnlock()
    75  
    76  	rc, err := d.reader(ctx, path, 0)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	defer rc.Close()
    81  
    82  	return ioutil.ReadAll(rc)
    83  }
    84  
    85  // PutContent stores the []byte content at a location designated by "path".
    86  func (d *driver) PutContent(ctx context.Context, p string, contents []byte) error {
    87  	d.mutex.Lock()
    88  	defer d.mutex.Unlock()
    89  
    90  	normalized := normalize(p)
    91  
    92  	f, err := d.root.mkfile(normalized)
    93  	if err != nil {
    94  		// TODO(stevvooe): Again, we need to clarify when this is not a
    95  		// directory in StorageDriver API.
    96  		return fmt.Errorf("not a file")
    97  	}
    98  
    99  	f.truncate()
   100  	f.WriteAt(contents, 0)
   101  
   102  	return nil
   103  }
   104  
   105  // Reader retrieves an io.ReadCloser for the content stored at "path" with a
   106  // given byte offset.
   107  func (d *driver) Reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
   108  	d.mutex.RLock()
   109  	defer d.mutex.RUnlock()
   110  
   111  	return d.reader(ctx, path, offset)
   112  }
   113  
   114  func (d *driver) reader(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
   115  	if offset < 0 {
   116  		return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset}
   117  	}
   118  
   119  	normalized := normalize(path)
   120  	found := d.root.find(normalized)
   121  
   122  	if found.path() != normalized {
   123  		return nil, storagedriver.PathNotFoundError{Path: path}
   124  	}
   125  
   126  	if found.isdir() {
   127  		return nil, fmt.Errorf("%q is a directory", path)
   128  	}
   129  
   130  	return ioutil.NopCloser(found.(*file).sectionReader(offset)), nil
   131  }
   132  
   133  // Writer returns a FileWriter which will store the content written to it
   134  // at the location designated by "path" after the call to Commit.
   135  func (d *driver) Writer(ctx context.Context, path string, append bool) (storagedriver.FileWriter, error) {
   136  	d.mutex.Lock()
   137  	defer d.mutex.Unlock()
   138  
   139  	normalized := normalize(path)
   140  
   141  	f, err := d.root.mkfile(normalized)
   142  	if err != nil {
   143  		return nil, fmt.Errorf("not a file")
   144  	}
   145  
   146  	if !append {
   147  		f.truncate()
   148  	}
   149  
   150  	return d.newWriter(f), nil
   151  }
   152  
   153  // Stat returns info about the provided path.
   154  func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) {
   155  	d.mutex.RLock()
   156  	defer d.mutex.RUnlock()
   157  
   158  	normalized := normalize(path)
   159  	found := d.root.find(normalized)
   160  
   161  	if found.path() != normalized {
   162  		return nil, storagedriver.PathNotFoundError{Path: path}
   163  	}
   164  
   165  	fi := storagedriver.FileInfoFields{
   166  		Path:    path,
   167  		IsDir:   found.isdir(),
   168  		ModTime: found.modtime(),
   169  	}
   170  
   171  	if !fi.IsDir {
   172  		fi.Size = int64(len(found.(*file).data))
   173  	}
   174  
   175  	return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
   176  }
   177  
   178  // List returns a list of the objects that are direct descendants of the given
   179  // path.
   180  func (d *driver) List(ctx context.Context, path string) ([]string, error) {
   181  	d.mutex.RLock()
   182  	defer d.mutex.RUnlock()
   183  
   184  	normalized := normalize(path)
   185  
   186  	found := d.root.find(normalized)
   187  
   188  	if !found.isdir() {
   189  		return nil, fmt.Errorf("not a directory") // TODO(stevvooe): Need error type for this...
   190  	}
   191  
   192  	entries, err := found.(*dir).list(normalized)
   193  
   194  	if err != nil {
   195  		switch err {
   196  		case errNotExists:
   197  			return nil, storagedriver.PathNotFoundError{Path: path}
   198  		case errIsNotDir:
   199  			return nil, fmt.Errorf("not a directory")
   200  		default:
   201  			return nil, err
   202  		}
   203  	}
   204  
   205  	return entries, nil
   206  }
   207  
   208  // Move moves an object stored at sourcePath to destPath, removing the original
   209  // object.
   210  func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error {
   211  	d.mutex.Lock()
   212  	defer d.mutex.Unlock()
   213  
   214  	normalizedSrc, normalizedDst := normalize(sourcePath), normalize(destPath)
   215  
   216  	err := d.root.move(normalizedSrc, normalizedDst)
   217  	switch err {
   218  	case errNotExists:
   219  		return storagedriver.PathNotFoundError{Path: destPath}
   220  	default:
   221  		return err
   222  	}
   223  }
   224  
   225  // Delete recursively deletes all objects stored at "path" and its subpaths.
   226  func (d *driver) Delete(ctx context.Context, path string) error {
   227  	d.mutex.Lock()
   228  	defer d.mutex.Unlock()
   229  
   230  	normalized := normalize(path)
   231  
   232  	err := d.root.delete(normalized)
   233  	switch err {
   234  	case errNotExists:
   235  		return storagedriver.PathNotFoundError{Path: path}
   236  	default:
   237  		return err
   238  	}
   239  }
   240  
   241  // URLFor returns a URL which may be used to retrieve the content stored at the given path.
   242  // May return an UnsupportedMethodErr in certain StorageDriver implementations.
   243  func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
   244  	return "", storagedriver.ErrUnsupportedMethod{}
   245  }
   246  
   247  // Walk traverses a filesystem defined within driver, starting
   248  // from the given path, calling f on each file
   249  func (d *driver) Walk(ctx context.Context, path string, f storagedriver.WalkFn) error {
   250  	return storagedriver.WalkFallback(ctx, d, path, f)
   251  }
   252  
   253  type writer struct {
   254  	d         *driver
   255  	f         *file
   256  	closed    bool
   257  	committed bool
   258  	cancelled bool
   259  }
   260  
   261  func (d *driver) newWriter(f *file) storagedriver.FileWriter {
   262  	return &writer{
   263  		d: d,
   264  		f: f,
   265  	}
   266  }
   267  
   268  func (w *writer) Write(p []byte) (int, error) {
   269  	if w.closed {
   270  		return 0, fmt.Errorf("already closed")
   271  	} else if w.committed {
   272  		return 0, fmt.Errorf("already committed")
   273  	} else if w.cancelled {
   274  		return 0, fmt.Errorf("already cancelled")
   275  	}
   276  
   277  	w.d.mutex.Lock()
   278  	defer w.d.mutex.Unlock()
   279  
   280  	return w.f.WriteAt(p, int64(len(w.f.data)))
   281  }
   282  
   283  func (w *writer) Size() int64 {
   284  	w.d.mutex.RLock()
   285  	defer w.d.mutex.RUnlock()
   286  
   287  	return int64(len(w.f.data))
   288  }
   289  
   290  func (w *writer) Close() error {
   291  	if w.closed {
   292  		return fmt.Errorf("already closed")
   293  	}
   294  	w.closed = true
   295  	return nil
   296  }
   297  
   298  func (w *writer) Cancel() error {
   299  	if w.closed {
   300  		return fmt.Errorf("already closed")
   301  	} else if w.committed {
   302  		return fmt.Errorf("already committed")
   303  	}
   304  	w.cancelled = true
   305  
   306  	w.d.mutex.Lock()
   307  	defer w.d.mutex.Unlock()
   308  
   309  	return w.d.root.delete(w.f.path())
   310  }
   311  
   312  func (w *writer) Commit() error {
   313  	if w.closed {
   314  		return fmt.Errorf("already closed")
   315  	} else if w.committed {
   316  		return fmt.Errorf("already committed")
   317  	} else if w.cancelled {
   318  		return fmt.Errorf("already cancelled")
   319  	}
   320  	w.committed = true
   321  	return nil
   322  }
   323  

View as plain text