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 }