code.gitea.io/gitea@v1.22.3/modules/setting/storage.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package setting 5 6 import ( 7 "errors" 8 "fmt" 9 "path/filepath" 10 "strings" 11 ) 12 13 // StorageType is a type of Storage 14 type StorageType string 15 16 const ( 17 // LocalStorageType is the type descriptor for local storage 18 LocalStorageType StorageType = "local" 19 // MinioStorageType is the type descriptor for minio storage 20 MinioStorageType StorageType = "minio" 21 ) 22 23 var storageTypes = []StorageType{ 24 LocalStorageType, 25 MinioStorageType, 26 } 27 28 // IsValidStorageType returns true if the given storage type is valid 29 func IsValidStorageType(storageType StorageType) bool { 30 for _, t := range storageTypes { 31 if t == storageType { 32 return true 33 } 34 } 35 return false 36 } 37 38 // MinioStorageConfig represents the configuration for a minio storage 39 type MinioStorageConfig struct { 40 Endpoint string `ini:"MINIO_ENDPOINT" json:",omitempty"` 41 AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID" json:",omitempty"` 42 SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty"` 43 Bucket string `ini:"MINIO_BUCKET" json:",omitempty"` 44 Location string `ini:"MINIO_LOCATION" json:",omitempty"` 45 BasePath string `ini:"MINIO_BASE_PATH" json:",omitempty"` 46 UseSSL bool `ini:"MINIO_USE_SSL"` 47 InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"` 48 ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty"` 49 ServeDirect bool `ini:"SERVE_DIRECT"` 50 } 51 52 // Storage represents configuration of storages 53 type Storage struct { 54 Type StorageType // local or minio 55 Path string `json:",omitempty"` // for local type 56 TemporaryPath string `json:",omitempty"` 57 MinioConfig MinioStorageConfig // for minio type 58 } 59 60 func (storage *Storage) ToShadowCopy() Storage { 61 shadowStorage := *storage 62 if shadowStorage.MinioConfig.AccessKeyID != "" { 63 shadowStorage.MinioConfig.AccessKeyID = "******" 64 } 65 if shadowStorage.MinioConfig.SecretAccessKey != "" { 66 shadowStorage.MinioConfig.SecretAccessKey = "******" 67 } 68 return shadowStorage 69 } 70 71 const storageSectionName = "storage" 72 73 func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection { 74 storageSec := rootCfg.Section(storageSectionName) 75 // Global Defaults 76 storageSec.Key("STORAGE_TYPE").MustString("local") 77 storageSec.Key("MINIO_ENDPOINT").MustString("localhost:9000") 78 storageSec.Key("MINIO_ACCESS_KEY_ID").MustString("") 79 storageSec.Key("MINIO_SECRET_ACCESS_KEY").MustString("") 80 storageSec.Key("MINIO_BUCKET").MustString("gitea") 81 storageSec.Key("MINIO_LOCATION").MustString("us-east-1") 82 storageSec.Key("MINIO_USE_SSL").MustBool(false) 83 storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false) 84 storageSec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default") 85 return storageSec 86 } 87 88 // getStorage will find target section and extra special section first and then read override 89 // items from extra section 90 func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*Storage, error) { 91 if name == "" { 92 return nil, errors.New("no name for storage") 93 } 94 95 targetSec, tp, err := getStorageTargetSection(rootCfg, name, typ, sec) 96 if err != nil { 97 return nil, err 98 } 99 100 overrideSec := getStorageOverrideSection(rootCfg, targetSec, sec, tp, name) 101 102 targetType := targetSec.Key("STORAGE_TYPE").String() 103 switch targetType { 104 case string(LocalStorageType): 105 return getStorageForLocal(targetSec, overrideSec, tp, name) 106 case string(MinioStorageType): 107 return getStorageForMinio(targetSec, overrideSec, tp, name) 108 default: 109 return nil, fmt.Errorf("unsupported storage type %q", targetType) 110 } 111 } 112 113 type targetSecType int 114 115 const ( 116 targetSecIsTyp targetSecType = iota // target section is [storage.type] which the type from parameter 117 targetSecIsStorage // target section is [storage] 118 targetSecIsDefault // target section is the default value 119 targetSecIsStorageWithName // target section is [storage.name] 120 targetSecIsSec // target section is from the name seciont [name] 121 ) 122 123 func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { 124 targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ) 125 if err != nil { 126 if !IsValidStorageType(StorageType(typ)) { 127 return nil, 0, fmt.Errorf("get section via storage type %q failed: %v", typ, err) 128 } 129 // if typ is a valid storage type, but there is no [storage.local] or [storage.minio] section 130 // it's not an error 131 return nil, 0, nil 132 } 133 134 targetType := targetSec.Key("STORAGE_TYPE").String() 135 if targetType == "" { 136 if !IsValidStorageType(StorageType(typ)) { 137 return nil, 0, fmt.Errorf("unknow storage type %q", typ) 138 } 139 targetSec.Key("STORAGE_TYPE").SetValue(typ) 140 } else if !IsValidStorageType(StorageType(targetType)) { 141 return nil, 0, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ) 142 } 143 144 return targetSec, targetSecIsTyp, nil 145 } 146 147 func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (ConfigSection, targetSecType, error) { 148 // check typ first 149 if typ == "" { 150 if sec != nil { // check sec's type secondly 151 typ = sec.Key("STORAGE_TYPE").String() 152 if IsValidStorageType(StorageType(typ)) { 153 if targetSec, _ := rootCfg.GetSection(storageSectionName + "." + typ); targetSec == nil { 154 return sec, targetSecIsSec, nil 155 } 156 } 157 } 158 } 159 160 if typ != "" { 161 targetSec, tp, err := getStorageSectionByType(rootCfg, typ) 162 if targetSec != nil || err != nil { 163 return targetSec, tp, err 164 } 165 } 166 167 // check stoarge name thirdly 168 targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name) 169 if targetSec != nil { 170 targetType := targetSec.Key("STORAGE_TYPE").String() 171 switch { 172 case targetType == "": 173 if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default 174 return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil 175 } 176 177 targetSec.Key("STORAGE_TYPE").SetValue("local") 178 default: 179 targetSec, tp, err := getStorageSectionByType(rootCfg, targetType) 180 if targetSec != nil || err != nil { 181 return targetSec, tp, err 182 } 183 } 184 185 return targetSec, targetSecIsStorageWithName, nil 186 } 187 188 return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil 189 } 190 191 // getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible 192 func getStorageOverrideSection(rootConfig ConfigProvider, targetSec, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection { 193 if targetSecType == targetSecIsSec { 194 return nil 195 } 196 197 if sec != nil { 198 return sec 199 } 200 201 if targetSecType != targetSecIsStorageWithName { 202 nameSec, _ := rootConfig.GetSection(storageSectionName + "." + name) 203 return nameSec 204 } 205 return nil 206 } 207 208 func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { 209 storage := Storage{ 210 Type: StorageType(targetSec.Key("STORAGE_TYPE").String()), 211 } 212 213 targetPath := ConfigSectionKeyString(targetSec, "PATH", "") 214 var fallbackPath string 215 if targetPath == "" { // no path 216 fallbackPath = filepath.Join(AppDataPath, name) 217 } else { 218 if tp == targetSecIsStorage || tp == targetSecIsDefault { 219 fallbackPath = filepath.Join(targetPath, name) 220 } else { 221 fallbackPath = targetPath 222 } 223 if !filepath.IsAbs(fallbackPath) { 224 fallbackPath = filepath.Join(AppDataPath, fallbackPath) 225 } 226 } 227 228 if overrideSec == nil { // no override section 229 storage.Path = fallbackPath 230 } else { 231 storage.Path = ConfigSectionKeyString(overrideSec, "PATH", "") 232 if storage.Path == "" { // overrideSec has no path 233 storage.Path = fallbackPath 234 } else if !filepath.IsAbs(storage.Path) { 235 if targetPath == "" { 236 storage.Path = filepath.Join(AppDataPath, storage.Path) 237 } else { 238 storage.Path = filepath.Join(targetPath, storage.Path) 239 } 240 } 241 } 242 243 checkOverlappedPath("[storage."+name+"].PATH", storage.Path) 244 245 return &storage, nil 246 } 247 248 func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { 249 var storage Storage 250 storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String()) 251 if err := targetSec.MapTo(&storage.MinioConfig); err != nil { 252 return nil, fmt.Errorf("map minio config failed: %v", err) 253 } 254 255 var defaultPath string 256 if storage.MinioConfig.BasePath != "" { 257 if tp == targetSecIsStorage || tp == targetSecIsDefault { 258 defaultPath = strings.TrimSuffix(storage.MinioConfig.BasePath, "/") + "/" + name + "/" 259 } else { 260 defaultPath = storage.MinioConfig.BasePath 261 } 262 } 263 if defaultPath == "" { 264 defaultPath = name + "/" 265 } 266 267 if overrideSec != nil { 268 storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) 269 storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", defaultPath) 270 storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) 271 } else { 272 storage.MinioConfig.BasePath = defaultPath 273 } 274 return &storage, nil 275 }