code.gitea.io/gitea@v1.22.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 any 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 Type = setting.StorageType 41 42 // NewStorageFunc is a function that creates a storage 43 type NewStorageFunc func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) 44 45 var storageMap = map[Type]NewStorageFunc{} 46 47 // RegisterStorageType registers a provided storage type with a function to create it 48 func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error)) { 49 storageMap[typ] = fn 50 } 51 52 // Object represents the object on the storage 53 type Object interface { 54 io.ReadCloser 55 io.Seeker 56 Stat() (os.FileInfo, error) 57 } 58 59 // ObjectStorage represents an object storage to handle a bucket and files 60 type ObjectStorage interface { 61 Open(path string) (Object, error) 62 // Save store a object, if size is unknown set -1 63 Save(path string, r io.Reader, size int64) (int64, error) 64 Stat(path string) (os.FileInfo, error) 65 Delete(path string) error 66 URL(path, name string) (*url.URL, error) 67 IterateObjects(path string, iterator func(path string, obj Object) error) error 68 } 69 70 // Copy copies a file from source ObjectStorage to dest ObjectStorage 71 func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, srcPath string) (int64, error) { 72 f, err := srcStorage.Open(srcPath) 73 if err != nil { 74 return 0, err 75 } 76 defer f.Close() 77 78 size := int64(-1) 79 fsinfo, err := f.Stat() 80 if err == nil { 81 size = fsinfo.Size() 82 } 83 84 return dstStorage.Save(dstPath, f, size) 85 } 86 87 // Clean delete all the objects in this storage 88 func Clean(storage ObjectStorage) error { 89 return storage.IterateObjects("", func(path string, obj Object) error { 90 _ = obj.Close() 91 return storage.Delete(path) 92 }) 93 } 94 95 // SaveFrom saves data to the ObjectStorage with path p from the callback 96 func SaveFrom(objStorage ObjectStorage, p string, callback func(w io.Writer) error) error { 97 pr, pw := io.Pipe() 98 defer pr.Close() 99 go func() { 100 defer pw.Close() 101 if err := callback(pw); err != nil { 102 _ = pw.CloseWithError(err) 103 } 104 }() 105 106 _, err := objStorage.Save(p, pr, -1) 107 return err 108 } 109 110 var ( 111 // Attachments represents attachments storage 112 Attachments ObjectStorage = uninitializedStorage 113 114 // LFS represents lfs storage 115 LFS ObjectStorage = uninitializedStorage 116 117 // Avatars represents user avatars storage 118 Avatars ObjectStorage = uninitializedStorage 119 // RepoAvatars represents repository avatars storage 120 RepoAvatars ObjectStorage = uninitializedStorage 121 122 // RepoArchives represents repository archives storage 123 RepoArchives ObjectStorage = uninitializedStorage 124 125 // Packages represents packages storage 126 Packages ObjectStorage = uninitializedStorage 127 128 // Actions represents actions storage 129 Actions ObjectStorage = uninitializedStorage 130 // Actions Artifacts represents actions artifacts storage 131 ActionsArtifacts ObjectStorage = uninitializedStorage 132 ) 133 134 // Init init the stoarge 135 func Init() error { 136 for _, f := range []func() error{ 137 initAttachments, 138 initAvatars, 139 initRepoAvatars, 140 initLFS, 141 initRepoArchives, 142 initPackages, 143 initActions, 144 } { 145 if err := f(); err != nil { 146 return err 147 } 148 } 149 return nil 150 } 151 152 // NewStorage takes a storage type and some config and returns an ObjectStorage or an error 153 func NewStorage(typStr Type, cfg *setting.Storage) (ObjectStorage, error) { 154 if len(typStr) == 0 { 155 typStr = setting.LocalStorageType 156 } 157 fn, ok := storageMap[typStr] 158 if !ok { 159 return nil, fmt.Errorf("Unsupported storage type: %s", typStr) 160 } 161 162 return fn(context.Background(), cfg) 163 } 164 165 func initAvatars() (err error) { 166 log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type) 167 Avatars, err = NewStorage(setting.Avatar.Storage.Type, setting.Avatar.Storage) 168 return err 169 } 170 171 func initAttachments() (err error) { 172 if !setting.Attachment.Enabled { 173 Attachments = discardStorage("Attachment isn't enabled") 174 return nil 175 } 176 log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type) 177 Attachments, err = NewStorage(setting.Attachment.Storage.Type, setting.Attachment.Storage) 178 return err 179 } 180 181 func initLFS() (err error) { 182 if !setting.LFS.StartServer { 183 LFS = discardStorage("LFS isn't enabled") 184 return nil 185 } 186 log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) 187 LFS, err = NewStorage(setting.LFS.Storage.Type, setting.LFS.Storage) 188 return err 189 } 190 191 func initRepoAvatars() (err error) { 192 log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type) 193 RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, setting.RepoAvatar.Storage) 194 return err 195 } 196 197 func initRepoArchives() (err error) { 198 log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type) 199 RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, setting.RepoArchive.Storage) 200 return err 201 } 202 203 func initPackages() (err error) { 204 if !setting.Packages.Enabled { 205 Packages = discardStorage("Packages isn't enabled") 206 return nil 207 } 208 log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type) 209 Packages, err = NewStorage(setting.Packages.Storage.Type, setting.Packages.Storage) 210 return err 211 } 212 213 func initActions() (err error) { 214 if !setting.Actions.Enabled { 215 Actions = discardStorage("Actions isn't enabled") 216 ActionsArtifacts = discardStorage("ActionsArtifacts isn't enabled") 217 return nil 218 } 219 log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type) 220 if Actions, err = NewStorage(setting.Actions.LogStorage.Type, setting.Actions.LogStorage); err != nil { 221 return err 222 } 223 log.Info("Initialising ActionsArtifacts storage with type: %s", setting.Actions.ArtifactStorage.Type) 224 ActionsArtifacts, err = NewStorage(setting.Actions.ArtifactStorage.Type, setting.Actions.ArtifactStorage) 225 return err 226 }