code.gitea.io/gitea@v1.19.3/modules/storage/storage.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package storage 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "io" 11 "net/url" 12 "os" 13 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/setting" 16 ) 17 18 // ErrURLNotSupported represents url is not supported 19 var ErrURLNotSupported = errors.New("url method not supported") 20 21 // ErrInvalidConfiguration is called when there is invalid configuration for a storage 22 type ErrInvalidConfiguration struct { 23 cfg interface{} 24 err error 25 } 26 27 func (err ErrInvalidConfiguration) Error() string { 28 if err.err != nil { 29 return fmt.Sprintf("Invalid Configuration Argument: %v: Error: %v", err.cfg, err.err) 30 } 31 return fmt.Sprintf("Invalid Configuration Argument: %v", err.cfg) 32 } 33 34 // IsErrInvalidConfiguration checks if an error is an ErrInvalidConfiguration 35 func IsErrInvalidConfiguration(err error) bool { 36 _, ok := err.(ErrInvalidConfiguration) 37 return ok 38 } 39 40 // Type is a type of Storage 41 type Type string 42 43 // NewStorageFunc is a function that creates a storage 44 type NewStorageFunc func(ctx context.Context, cfg interface{}) (ObjectStorage, error) 45 46 var storageMap = map[Type]NewStorageFunc{} 47 48 // RegisterStorageType registers a provided storage type with a function to create it 49 func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg interface{}) (ObjectStorage, error)) { 50 storageMap[typ] = fn 51 } 52 53 // Object represents the object on the storage 54 type Object interface { 55 io.ReadCloser 56 io.Seeker 57 Stat() (os.FileInfo, error) 58 } 59 60 // ObjectStorage represents an object storage to handle a bucket and files 61 type ObjectStorage interface { 62 Open(path string) (Object, error) 63 // Save store a object, if size is unknown set -1 64 Save(path string, r io.Reader, size int64) (int64, error) 65 Stat(path string) (os.FileInfo, error) 66 Delete(path string) error 67 URL(path, name string) (*url.URL, error) 68 IterateObjects(func(path string, obj Object) error) error 69 } 70 71 // Copy copies a file from source ObjectStorage to dest ObjectStorage 72 func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, srcPath string) (int64, error) { 73 f, err := srcStorage.Open(srcPath) 74 if err != nil { 75 return 0, err 76 } 77 defer f.Close() 78 79 size := int64(-1) 80 fsinfo, err := f.Stat() 81 if err == nil { 82 size = fsinfo.Size() 83 } 84 85 return dstStorage.Save(dstPath, f, size) 86 } 87 88 // Clean delete all the objects in this storage 89 func Clean(storage ObjectStorage) error { 90 return storage.IterateObjects(func(path string, obj Object) error { 91 _ = obj.Close() 92 return storage.Delete(path) 93 }) 94 } 95 96 // SaveFrom saves data to the ObjectStorage with path p from the callback 97 func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) error) error { 98 pr, pw := io.Pipe() 99 defer pr.Close() 100 go func() { 101 defer pw.Close() 102 if err := callback(pw); err != nil { 103 _ = pw.CloseWithError(err) 104 } 105 }() 106 107 _, err := objStorage.Save(p, pr, -1) 108 return err 109 } 110 111 var ( 112 // Attachments represents attachments storage 113 Attachments ObjectStorage = uninitializedStorage 114 115 // LFS represents lfs storage 116 LFS ObjectStorage = uninitializedStorage 117 118 // Avatars represents user avatars storage 119 Avatars ObjectStorage = uninitializedStorage 120 // RepoAvatars represents repository avatars storage 121 RepoAvatars ObjectStorage = uninitializedStorage 122 123 // RepoArchives represents repository archives storage 124 RepoArchives ObjectStorage = uninitializedStorage 125 126 // Packages represents packages storage 127 Packages ObjectStorage = uninitializedStorage 128 129 // Actions represents actions storage 130 Actions ObjectStorage = uninitializedStorage 131 ) 132 133 // Init init the stoarge 134 func Init() error { 135 for _, f := range []func() error{ 136 initAttachments, 137 initAvatars, 138 initRepoAvatars, 139 initLFS, 140 initRepoArchives, 141 initPackages, 142 initActions, 143 } { 144 if err := f(); err != nil { 145 return err 146 } 147 } 148 return nil 149 } 150 151 // NewStorage takes a storage type and some config and returns an ObjectStorage or an error 152 func NewStorage(typStr string, cfg interface{}) (ObjectStorage, error) { 153 if len(typStr) == 0 { 154 typStr = string(LocalStorageType) 155 } 156 fn, ok := storageMap[Type(typStr)] 157 if !ok { 158 return nil, fmt.Errorf("Unsupported storage type: %s", typStr) 159 } 160 161 return fn(context.Background(), cfg) 162 } 163 164 func initAvatars() (err error) { 165 log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type) 166 Avatars, err = NewStorage(setting.Avatar.Storage.Type, &setting.Avatar.Storage) 167 return err 168 } 169 170 func initAttachments() (err error) { 171 if !setting.Attachment.Enabled { 172 Attachments = discardStorage("Attachment isn't enabled") 173 return nil 174 } 175 log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type) 176 Attachments, err = NewStorage(setting.Attachment.Storage.Type, &setting.Attachment.Storage) 177 return err 178 } 179 180 func initLFS() (err error) { 181 if !setting.LFS.StartServer { 182 LFS = discardStorage("LFS isn't enabled") 183 return nil 184 } 185 log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) 186 LFS, err = NewStorage(setting.LFS.Storage.Type, &setting.LFS.Storage) 187 return err 188 } 189 190 func initRepoAvatars() (err error) { 191 log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type) 192 RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, &setting.RepoAvatar.Storage) 193 return err 194 } 195 196 func initRepoArchives() (err error) { 197 log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type) 198 RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, &setting.RepoArchive.Storage) 199 return err 200 } 201 202 func initPackages() (err error) { 203 if !setting.Packages.Enabled { 204 Packages = discardStorage("Packages isn't enabled") 205 return nil 206 } 207 log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type) 208 Packages, err = NewStorage(setting.Packages.Storage.Type, &setting.Packages.Storage) 209 return err 210 } 211 212 func initActions() (err error) { 213 if !setting.Actions.Enabled { 214 Actions = discardStorage("Actions isn't enabled") 215 return nil 216 } 217 log.Info("Initialising Actions storage with type: %s", setting.Actions.Storage.Type) 218 Actions, err = NewStorage(setting.Actions.Storage.Type, &setting.Actions.Storage) 219 return err 220 }