gitea源码

storage.go 12KB

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