github.com/lusis/distribution@v2.0.1+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 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(path string) ([]byte, error) { 73 d.mutex.RLock() 74 defer d.mutex.RUnlock() 75 76 rc, err := d.ReadStream(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(p string, contents []byte) error { 87 d.mutex.Lock() 88 defer d.mutex.Unlock() 89 90 f, err := d.root.mkfile(p) 91 if err != nil { 92 // TODO(stevvooe): Again, we need to clarify when this is not a 93 // directory in StorageDriver API. 94 return fmt.Errorf("not a file") 95 } 96 97 f.truncate() 98 f.WriteAt(contents, 0) 99 100 return nil 101 } 102 103 // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a 104 // given byte offset. 105 func (d *driver) ReadStream(path string, offset int64) (io.ReadCloser, error) { 106 d.mutex.RLock() 107 defer d.mutex.RUnlock() 108 109 if offset < 0 { 110 return nil, storagedriver.InvalidOffsetError{Path: path, Offset: offset} 111 } 112 113 path = normalize(path) 114 found := d.root.find(path) 115 116 if found.path() != path { 117 return nil, storagedriver.PathNotFoundError{Path: path} 118 } 119 120 if found.isdir() { 121 return nil, fmt.Errorf("%q is a directory", path) 122 } 123 124 return ioutil.NopCloser(found.(*file).sectionReader(offset)), nil 125 } 126 127 // WriteStream stores the contents of the provided io.ReadCloser at a location 128 // designated by the given path. 129 func (d *driver) WriteStream(path string, offset int64, reader io.Reader) (nn int64, err error) { 130 d.mutex.Lock() 131 defer d.mutex.Unlock() 132 133 if offset < 0 { 134 return 0, storagedriver.InvalidOffsetError{Path: path, Offset: offset} 135 } 136 137 normalized := normalize(path) 138 139 f, err := d.root.mkfile(normalized) 140 if err != nil { 141 return 0, fmt.Errorf("not a file") 142 } 143 144 // Unlock while we are reading from the source, in case we are reading 145 // from the same mfs instance. This can be fixed by a more granular 146 // locking model. 147 d.mutex.Unlock() 148 d.mutex.RLock() // Take the readlock to block other writers. 149 var buf bytes.Buffer 150 151 nn, err = buf.ReadFrom(reader) 152 if err != nil { 153 // TODO(stevvooe): This condition is odd and we may need to clarify: 154 // we've read nn bytes from reader but have written nothing to the 155 // backend. What is the correct return value? Really, the caller needs 156 // to know that the reader has been advanced and reattempting the 157 // operation is incorrect. 158 d.mutex.RUnlock() 159 d.mutex.Lock() 160 return nn, err 161 } 162 163 d.mutex.RUnlock() 164 d.mutex.Lock() 165 f.WriteAt(buf.Bytes(), offset) 166 return nn, err 167 } 168 169 // Stat returns info about the provided path. 170 func (d *driver) Stat(path string) (storagedriver.FileInfo, error) { 171 d.mutex.RLock() 172 defer d.mutex.RUnlock() 173 174 normalized := normalize(path) 175 found := d.root.find(path) 176 177 if found.path() != normalized { 178 return nil, storagedriver.PathNotFoundError{Path: path} 179 } 180 181 fi := storagedriver.FileInfoFields{ 182 Path: path, 183 IsDir: found.isdir(), 184 ModTime: found.modtime(), 185 } 186 187 if !fi.IsDir { 188 fi.Size = int64(len(found.(*file).data)) 189 } 190 191 return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil 192 } 193 194 // List returns a list of the objects that are direct descendants of the given 195 // path. 196 func (d *driver) List(path string) ([]string, error) { 197 d.mutex.RLock() 198 defer d.mutex.RUnlock() 199 200 normalized := normalize(path) 201 202 found := d.root.find(normalized) 203 204 if !found.isdir() { 205 return nil, fmt.Errorf("not a directory") // TODO(stevvooe): Need error type for this... 206 } 207 208 entries, err := found.(*dir).list(normalized) 209 210 if err != nil { 211 switch err { 212 case errNotExists: 213 return nil, storagedriver.PathNotFoundError{Path: path} 214 case errIsNotDir: 215 return nil, fmt.Errorf("not a directory") 216 default: 217 return nil, err 218 } 219 } 220 221 return entries, nil 222 } 223 224 // Move moves an object stored at sourcePath to destPath, removing the original 225 // object. 226 func (d *driver) Move(sourcePath string, destPath string) error { 227 d.mutex.Lock() 228 defer d.mutex.Unlock() 229 230 normalizedSrc, normalizedDst := normalize(sourcePath), normalize(destPath) 231 232 err := d.root.move(normalizedSrc, normalizedDst) 233 switch err { 234 case errNotExists: 235 return storagedriver.PathNotFoundError{Path: destPath} 236 default: 237 return err 238 } 239 } 240 241 // Delete recursively deletes all objects stored at "path" and its subpaths. 242 func (d *driver) Delete(path string) error { 243 d.mutex.Lock() 244 defer d.mutex.Unlock() 245 246 normalized := normalize(path) 247 248 err := d.root.delete(normalized) 249 switch err { 250 case errNotExists: 251 return storagedriver.PathNotFoundError{Path: path} 252 default: 253 return err 254 } 255 } 256 257 // URLFor returns a URL which may be used to retrieve the content stored at the given path. 258 // May return an UnsupportedMethodErr in certain StorageDriver implementations. 259 func (d *driver) URLFor(path string, options map[string]interface{}) (string, error) { 260 return "", storagedriver.ErrUnsupportedMethod 261 }