loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix all possible setting error related storages and added some tests (#23911)

Follow up #22405

Fix #20703

This PR rewrites storage configuration read sequences with some breaks
and tests. It becomes more strict than before and also fixed some
inherit problems.

- Move storage's MinioConfig struct into setting, so after the
configuration loading, the values will be stored into the struct but not
still on some section.
- All storages configurations should be stored on one section,
configuration items cannot be overrided by multiple sections. The
prioioty of configuration is `[attachment]` > `[storage.attachments]` |
`[storage.customized]` > `[storage]` > `default`
- For extra override configuration items, currently are `SERVE_DIRECT`,
`MINIO_BASE_PATH`, `MINIO_BUCKET`, which could be configured in another
section. The prioioty of the override configuration is `[attachment]` >
`[storage.attachments]` > `default`.
- Add more tests for storages configurations.
- Update the storage documentations.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>

authored by

Lunny Xiao
wxiaoguang
and committed by
GitHub
d6dd6d64 dc0a7168

+1146 -446
+3 -3
cmd/dump.go
··· 353 353 } 354 354 355 355 excludes = append(excludes, setting.RepoRootPath) 356 - excludes = append(excludes, setting.LFS.Path) 357 - excludes = append(excludes, setting.Attachment.Path) 358 - excludes = append(excludes, setting.Packages.Path) 356 + excludes = append(excludes, setting.LFS.Storage.Path) 357 + excludes = append(excludes, setting.Attachment.Storage.Path) 358 + excludes = append(excludes, setting.Packages.Storage.Path) 359 359 excludes = append(excludes, setting.Log.RootPath) 360 360 excludes = append(excludes, absFileName) 361 361 if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
+15 -13
cmd/migrate_storage.go
··· 179 179 switch strings.ToLower(ctx.String("storage")) { 180 180 case "": 181 181 fallthrough 182 - case string(storage.LocalStorageType): 182 + case string(setting.LocalStorageType): 183 183 p := ctx.String("path") 184 184 if p == "" { 185 185 log.Fatal("Path must be given when storage is loal") ··· 187 187 } 188 188 dstStorage, err = storage.NewLocalStorage( 189 189 stdCtx, 190 - storage.LocalStorageConfig{ 190 + &setting.Storage{ 191 191 Path: p, 192 192 }) 193 - case string(storage.MinioStorageType): 193 + case string(setting.MinioStorageType): 194 194 dstStorage, err = storage.NewMinioStorage( 195 195 stdCtx, 196 - storage.MinioStorageConfig{ 197 - Endpoint: ctx.String("minio-endpoint"), 198 - AccessKeyID: ctx.String("minio-access-key-id"), 199 - SecretAccessKey: ctx.String("minio-secret-access-key"), 200 - Bucket: ctx.String("minio-bucket"), 201 - Location: ctx.String("minio-location"), 202 - BasePath: ctx.String("minio-base-path"), 203 - UseSSL: ctx.Bool("minio-use-ssl"), 204 - InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"), 205 - ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"), 196 + &setting.Storage{ 197 + MinioConfig: setting.MinioStorageConfig{ 198 + Endpoint: ctx.String("minio-endpoint"), 199 + AccessKeyID: ctx.String("minio-access-key-id"), 200 + SecretAccessKey: ctx.String("minio-secret-access-key"), 201 + Bucket: ctx.String("minio-bucket"), 202 + Location: ctx.String("minio-location"), 203 + BasePath: ctx.String("minio-base-path"), 204 + UseSSL: ctx.Bool("minio-use-ssl"), 205 + InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"), 206 + ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"), 207 + }, 206 208 }) 207 209 default: 208 210 return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
+2 -1
cmd/migrate_storage_test.go
··· 13 13 "code.gitea.io/gitea/models/unittest" 14 14 user_model "code.gitea.io/gitea/models/user" 15 15 packages_module "code.gitea.io/gitea/modules/packages" 16 + "code.gitea.io/gitea/modules/setting" 16 17 "code.gitea.io/gitea/modules/storage" 17 18 packages_service "code.gitea.io/gitea/services/packages" 18 19 ··· 57 58 58 59 dstStorage, err := storage.NewLocalStorage( 59 60 ctx, 60 - storage.LocalStorageConfig{ 61 + &setting.Storage{ 61 62 Path: p, 62 63 }) 63 64 assert.NoError(t, err)
+21
custom/conf/app.example.ini
··· 2392 2392 ;; Enable/Disable package registry capabilities 2393 2393 ;ENABLED = true 2394 2394 ;; 2395 + ;STORAGE_TYPE = local 2396 + ;; override the minio base path if storage type is minio 2397 + ;MINIO_BASE_PATH = packages/ 2398 + ;; 2395 2399 ;; Path for chunked uploads. Defaults to APP_DATA_PATH + `tmp/package-upload` 2396 2400 ;CHUNKED_UPLOAD_PATH = tmp/package-upload 2397 2401 ;; ··· 2454 2458 2455 2459 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2456 2460 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2461 + ;; repo-archive storage will override storage 2462 + ;; 2463 + ;[repo-archive] 2464 + ;STORAGE_TYPE = local 2465 + ;; 2466 + ;; Where your lfs files reside, default is data/lfs. 2467 + ;PATH = data/repo-archive 2468 + ;; 2469 + ;; override the minio base path if storage type is minio 2470 + ;MINIO_BASE_PATH = repo-archive/ 2471 + 2472 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2473 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2457 2474 ;; settings for repository archives, will override storage setting 2458 2475 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2459 2476 ;[storage.repo-archive] ··· 2471 2488 ;; 2472 2489 ;; Where your lfs files reside, default is data/lfs. 2473 2490 ;PATH = data/lfs 2491 + ;; 2492 + ;; override the minio base path if storage type is minio 2493 + ;MINIO_BASE_PATH = lfs/ 2474 2494 2475 2495 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2476 2496 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ··· 2520 2540 ; [actions] 2521 2541 ;; Enable/Disable actions capabilities 2522 2542 ;ENABLED = false 2543 + ;; 2523 2544 ;; Default address to get action plugins, e.g. the default value means downloading from "https://gitea.com/actions/checkout" for "uses: actions/checkout@v3" 2524 2545 ;DEFAULT_ACTIONS_URL = https://gitea.com 2525 2546
+57 -4
docs/content/doc/administration/config-cheat-sheet.en-us.md
··· 1254 1254 1255 1255 ## Storage (`storage`) 1256 1256 1257 - Default storage configuration for attachments, lfs, avatars and etc. 1257 + Default storage configuration for attachments, lfs, avatars, repo-avatars, repo-archive, packages, actions_log, actions_artifact. 1258 1258 1259 + - `STORAGE_TYPE`: **local**: Storage type, `local` for local disk or `minio` for s3 compatible object storage service. 1259 1260 - `SERVE_DIRECT`: **false**: Allows the storage driver to redirect to authenticated URLs to serve files directly. Currently, only Minio/S3 is supported via signed URLs, local does nothing. 1260 1261 - `MINIO_ENDPOINT`: **localhost:9000**: Minio endpoint to connect only available when `STORAGE_TYPE` is `minio` 1261 1262 - `MINIO_ACCESS_KEY_ID`: Minio accessKeyID to connect only available when `STORAGE_TYPE` is `minio` ··· 1265 1266 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio` 1266 1267 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio` 1267 1268 1268 - And you can also define a customize storage like below: 1269 + The recommanded storage configuration for minio like below: 1270 + 1271 + ```ini 1272 + [storage] 1273 + STORAGE_TYPE = minio 1274 + ; Minio endpoint to connect only available when STORAGE_TYPE is `minio` 1275 + MINIO_ENDPOINT = localhost:9000 1276 + ; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio` 1277 + MINIO_ACCESS_KEY_ID = 1278 + ; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio` 1279 + MINIO_SECRET_ACCESS_KEY = 1280 + ; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio` 1281 + MINIO_BUCKET = gitea 1282 + ; Minio location to create bucket only available when STORAGE_TYPE is `minio` 1283 + MINIO_LOCATION = us-east-1 1284 + ; Minio enabled ssl only available when STORAGE_TYPE is `minio` 1285 + MINIO_USE_SSL = false 1286 + ; Minio skip SSL verification available when STORAGE_TYPE is `minio` 1287 + MINIO_INSECURE_SKIP_VERIFY = false 1288 + SERVE_DIRECT = true 1289 + ``` 1290 + 1291 + Defaultly every storage has their default base path like below 1292 + 1293 + | storage | default base path | 1294 + | ----------------- | ------------------ | 1295 + | attachments | attachments/ | 1296 + | lfs | lfs/ | 1297 + | avatars | avatars/ | 1298 + | repo-avatars | repo-avatars/ | 1299 + | repo-archive | repo-archive/ | 1300 + | packages | packages/ | 1301 + | actions_log | actions_log/ | 1302 + | actions_artifacts | actions_artifacts/ | 1303 + 1304 + And bucket, basepath or `SERVE_DIRECT` could be special or overrided, if you want to use a different you can: 1269 1305 1270 1306 ```ini 1307 + [storage.actions_log] 1308 + MINIO_BUCKET = gitea_actions_log 1309 + SERVE_DIRECT = true 1310 + MINIO_BASE_PATH = my_actions_log/ ; default is actions_log/ if blank 1311 + ``` 1312 + 1313 + If you want to customerize a different storage for `lfs` if above default storage defined 1314 + 1315 + ```ini 1316 + [lfs] 1317 + STORAGE_TYPE = my_minio 1318 + 1271 1319 [storage.my_minio] 1272 1320 STORAGE_TYPE = minio 1273 1321 ; Minio endpoint to connect only available when STORAGE_TYPE is `minio` ··· 1285 1333 ; Minio skip SSL verification available when STORAGE_TYPE is `minio` 1286 1334 MINIO_INSECURE_SKIP_VERIFY = false 1287 1335 ``` 1288 - 1289 - And used by `[attachment]`, `[lfs]` and etc. as `STORAGE_TYPE`. 1290 1336 1291 1337 ## Repository Archive Storage (`storage.repo-archive`) 1292 1338 ··· 1306 1352 - `MINIO_USE_SSL`: **false**: Minio enabled ssl only available when `STORAGE_TYPE` is `minio` 1307 1353 - `MINIO_INSECURE_SKIP_VERIFY`: **false**: Minio skip SSL verification available when STORAGE_TYPE is `minio` 1308 1354 1355 + ## Repository Archives (`repo-archive`) 1356 + 1357 + - `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` 1358 + - `MINIO_BASE_PATH`: **repo-archive/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio` 1359 + 1309 1360 ## Proxy (`proxy`) 1310 1361 1311 1362 - `PROXY_ENABLED`: **false**: Enable the proxy if true, all requests to external via HTTP will be affected, if false, no proxy will be used even environment http_proxy/https_proxy ··· 1324 1375 1325 1376 - `ENABLED`: **false**: Enable/Disable actions capabilities 1326 1377 - `DEFAULT_ACTIONS_URL`: **https://gitea.com**: Default address to get action plugins, e.g. the default value means downloading from "<https://gitea.com/actions/checkout>" for "uses: actions/checkout@v3" 1378 + - `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` 1379 + - `MINIO_BASE_PATH`: **actions_log/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio` 1327 1380 1328 1381 `DEFAULT_ACTIONS_URL` indicates where should we find the relative path action plugin. i.e. when use an action in a workflow file like 1329 1382
+53 -4
docs/content/doc/administration/config-cheat-sheet.zh-cn.md
··· 414 414 415 415 ## Storage (`storage`) 416 416 417 - Attachments, lfs, avatars and etc 的默认存储配置。 417 + Attachments, lfs, avatars, repo-avatars, repo-archive, packages, actions_log, actions_artifact 的默认存储配置。 418 418 419 419 - `STORAGE_TYPE`: **local**: 附件存储类型,`local` 将存储到本地文件夹, `minio` 将存储到 s3 兼容的对象存储服务中。 420 420 - `SERVE_DIRECT`: **false**: 允许直接重定向到存储系统。当前,仅 Minio/S3 是支持的。 ··· 425 425 - `MINIO_LOCATION`: **us-east-1**: Minio location to create bucket,仅当 `STORAGE_TYPE` 是 `minio` 时有效。 426 426 - `MINIO_USE_SSL`: **false**: Minio enabled ssl,仅当 `STORAGE_TYPE` 是 `minio` 时有效。 427 427 428 - 你也可以自定义一个存储的名字如下: 428 + 以下为推荐的 recommanded storage configuration for minio like below: 429 + 430 + ```ini 431 + [storage] 432 + STORAGE_TYPE = minio 433 + ; uncomment when STORAGE_TYPE = local 434 + ; PATH = storage root path 435 + ; Minio endpoint to connect only available when STORAGE_TYPE is `minio` 436 + MINIO_ENDPOINT = localhost:9000 437 + ; Minio accessKeyID to connect only available when STORAGE_TYPE is `minio` 438 + MINIO_ACCESS_KEY_ID = 439 + ; Minio secretAccessKey to connect only available when STORAGE_TYPE is `minio` 440 + MINIO_SECRET_ACCESS_KEY = 441 + ; Minio bucket to store the attachments only available when STORAGE_TYPE is `minio` 442 + MINIO_BUCKET = gitea 443 + ; Minio location to create bucket only available when STORAGE_TYPE is `minio` 444 + MINIO_LOCATION = us-east-1 445 + ; Minio enabled ssl only available when STORAGE_TYPE is `minio` 446 + MINIO_USE_SSL = false 447 + ; Minio skip SSL verification available when STORAGE_TYPE is `minio` 448 + MINIO_INSECURE_SKIP_VERIFY = false 449 + SERVE_DIRECT = true 450 + ``` 451 + 452 + 默认的,每一个存储都会有各自默认的 BasePath 在同一个minio中,默认值如下: 453 + 454 + | storage | default base path | 455 + | ----------------- | ------------------ | 456 + | attachments | attachments/ | 457 + | lfs | lfs/ | 458 + | avatars | avatars/ | 459 + | repo-avatars | repo-avatars/ | 460 + | repo-archive | repo-archive/ | 461 + | packages | packages/ | 462 + | actions_log | actions_log/ | 463 + | actions_artifacts | actions_artifacts/ | 464 + 465 + 同时 bucket, basepath or `SERVE_DIRECT` 是可以被覆写的,像如下所示: 429 466 430 467 ```ini 468 + [storage.actions_log] 469 + MINIO_BUCKET = gitea_actions_log 470 + SERVE_DIRECT = true 471 + MINIO_BASE_PATH = my_actions_log/ ; default is actions_log/ if blank 472 + ``` 473 + 474 + 当然你也可以完全自定义,像如下 475 + 476 + ```ini 477 + [lfs] 478 + STORAGE_TYPE = my_minio 479 + MINIO_BASE_PATH = my_lfs_basepath 480 + 431 481 [storage.my_minio] 432 482 STORAGE_TYPE = minio 433 483 ; Minio endpoint to connect only available when STORAGE_TYPE is `minio` ··· 444 494 MINIO_USE_SSL = false 445 495 ; Minio skip SSL verification available when STORAGE_TYPE is `minio` 446 496 MINIO_INSECURE_SKIP_VERIFY = false 497 + SERVE_DIRECT = true 447 498 ``` 448 - 449 - 然后你在 `[attachment]`, `[lfs]` 等中可以把这个名字用作 `STORAGE_TYPE` 的值。 450 499 451 500 ## Repository Archive Storage (`storage.repo-archive`) 452 501
+1 -1
models/migrations/v1_10/v96.go
··· 53 53 54 54 for _, attachment := range attachments { 55 55 uuid := attachment.UUID 56 - if err := util.RemoveAll(filepath.Join(setting.Attachment.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { 56 + if err := util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { 57 57 return err 58 58 } 59 59 }
+1 -1
models/migrations/v1_11/v112.go
··· 30 30 31 31 for i := 0; i < len(attachments); i++ { 32 32 uuid := attachments[i].UUID 33 - if err = util.RemoveAll(filepath.Join(setting.Attachment.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { 33 + if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { 34 34 fmt.Printf("Error: %v", err) //nolint:forbidigo 35 35 } 36 36 }
+4 -4
models/migrations/v1_11/v115.go
··· 61 61 for _, user := range users { 62 62 oldAvatar := user.Avatar 63 63 64 - if stat, err := os.Stat(filepath.Join(setting.Avatar.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() { 64 + if stat, err := os.Stat(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)); err != nil || !stat.Mode().IsRegular() { 65 65 if err == nil { 66 66 err = fmt.Errorf("Error: \"%s\" is not a regular file", oldAvatar) 67 67 } ··· 86 86 return fmt.Errorf("[user: %s] user table update: %w", user.LowerName, err) 87 87 } 88 88 89 - deleteList.Add(filepath.Join(setting.Avatar.Path, oldAvatar)) 89 + deleteList.Add(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)) 90 90 migrated++ 91 91 select { 92 92 case <-ticker.C: ··· 135 135 // copyOldAvatarToNewLocation copies oldAvatar to newAvatarLocation 136 136 // and returns newAvatar location 137 137 func copyOldAvatarToNewLocation(userID int64, oldAvatar string) (string, error) { 138 - fr, err := os.Open(filepath.Join(setting.Avatar.Path, oldAvatar)) 138 + fr, err := os.Open(filepath.Join(setting.Avatar.Storage.Path, oldAvatar)) 139 139 if err != nil { 140 140 return "", fmt.Errorf("os.Open: %w", err) 141 141 } ··· 151 151 return newAvatar, nil 152 152 } 153 153 154 - if err := os.WriteFile(filepath.Join(setting.Avatar.Path, newAvatar), data, 0o666); err != nil { 154 + if err := os.WriteFile(filepath.Join(setting.Avatar.Storage.Path, newAvatar), data, 0o666); err != nil { 155 155 return "", fmt.Errorf("os.WriteFile: %w", err) 156 156 } 157 157
+17 -10
modules/setting/actions.go
··· 4 4 package setting 5 5 6 6 import ( 7 - "code.gitea.io/gitea/modules/log" 7 + "fmt" 8 8 ) 9 9 10 10 // Actions settings 11 11 var ( 12 12 Actions = struct { 13 - LogStorage Storage // how the created logs should be stored 14 - ArtifactStorage Storage // how the created artifacts should be stored 13 + LogStorage *Storage // how the created logs should be stored 14 + ArtifactStorage *Storage // how the created artifacts should be stored 15 15 Enabled bool 16 16 DefaultActionsURL string `ini:"DEFAULT_ACTIONS_URL"` 17 17 }{ ··· 20 20 } 21 21 ) 22 22 23 - func loadActionsFrom(rootCfg ConfigProvider) { 23 + func loadActionsFrom(rootCfg ConfigProvider) error { 24 24 sec := rootCfg.Section("actions") 25 - if err := sec.MapTo(&Actions); err != nil { 26 - log.Fatal("Failed to map Actions settings: %v", err) 25 + err := sec.MapTo(&Actions) 26 + if err != nil { 27 + return fmt.Errorf("failed to map Actions settings: %v", err) 28 + } 29 + 30 + // don't support to read configuration from [actions] 31 + Actions.LogStorage, err = getStorage(rootCfg, "actions_log", "", nil) 32 + if err != nil { 33 + return err 27 34 } 28 35 29 - actionsSec := rootCfg.Section("actions.artifacts") 30 - storageType := actionsSec.Key("STORAGE_TYPE").MustString("") 36 + actionsSec, _ := rootCfg.GetSection("actions.artifacts") 37 + 38 + Actions.ArtifactStorage, err = getStorage(rootCfg, "actions_artifacts", "", actionsSec) 31 39 32 - Actions.LogStorage = getStorage(rootCfg, "actions_log", "", nil) 33 - Actions.ArtifactStorage = getStorage(rootCfg, "actions_artifacts", storageType, actionsSec) 40 + return err 34 41 }
+97
modules/setting/actions_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package setting 5 + 6 + import ( 7 + "path/filepath" 8 + "testing" 9 + 10 + "github.com/stretchr/testify/assert" 11 + ) 12 + 13 + func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) { 14 + iniStr := ` 15 + [storage] 16 + STORAGE_TYPE = minio 17 + ` 18 + cfg, err := NewConfigProviderFromData(iniStr) 19 + assert.NoError(t, err) 20 + assert.NoError(t, loadActionsFrom(cfg)) 21 + 22 + assert.EqualValues(t, "minio", Actions.LogStorage.Type) 23 + assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) 24 + assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) 25 + assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) 26 + 27 + iniStr = ` 28 + [storage.actions_log] 29 + STORAGE_TYPE = minio 30 + ` 31 + cfg, err = NewConfigProviderFromData(iniStr) 32 + assert.NoError(t, err) 33 + assert.NoError(t, loadActionsFrom(cfg)) 34 + 35 + assert.EqualValues(t, "minio", Actions.LogStorage.Type) 36 + assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) 37 + assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) 38 + assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) 39 + 40 + iniStr = ` 41 + [storage.actions_log] 42 + STORAGE_TYPE = my_storage 43 + 44 + [storage.my_storage] 45 + STORAGE_TYPE = minio 46 + ` 47 + cfg, err = NewConfigProviderFromData(iniStr) 48 + assert.NoError(t, err) 49 + assert.NoError(t, loadActionsFrom(cfg)) 50 + 51 + assert.EqualValues(t, "minio", Actions.LogStorage.Type) 52 + assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) 53 + assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) 54 + assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) 55 + 56 + iniStr = ` 57 + [storage.actions_artifacts] 58 + STORAGE_TYPE = my_storage 59 + 60 + [storage.my_storage] 61 + STORAGE_TYPE = minio 62 + ` 63 + cfg, err = NewConfigProviderFromData(iniStr) 64 + assert.NoError(t, err) 65 + assert.NoError(t, loadActionsFrom(cfg)) 66 + 67 + assert.EqualValues(t, "local", Actions.LogStorage.Type) 68 + assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) 69 + assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) 70 + assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) 71 + 72 + iniStr = ` 73 + [storage.actions_artifacts] 74 + STORAGE_TYPE = my_storage 75 + 76 + [storage.my_storage] 77 + STORAGE_TYPE = minio 78 + ` 79 + cfg, err = NewConfigProviderFromData(iniStr) 80 + assert.NoError(t, err) 81 + assert.NoError(t, loadActionsFrom(cfg)) 82 + 83 + assert.EqualValues(t, "local", Actions.LogStorage.Type) 84 + assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) 85 + assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) 86 + assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) 87 + 88 + iniStr = `` 89 + cfg, err = NewConfigProviderFromData(iniStr) 90 + assert.NoError(t, err) 91 + assert.NoError(t, loadActionsFrom(cfg)) 92 + 93 + assert.EqualValues(t, "local", Actions.LogStorage.Type) 94 + assert.EqualValues(t, "actions_log", filepath.Base(Actions.LogStorage.Path)) 95 + assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) 96 + assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) 97 + }
+12 -10
modules/setting/attachment.go
··· 5 5 6 6 // Attachment settings 7 7 var Attachment = struct { 8 - Storage 8 + Storage *Storage 9 9 AllowedTypes string 10 10 MaxSize int64 11 11 MaxFiles int 12 12 Enabled bool 13 13 }{ 14 - Storage: Storage{ 15 - ServeDirect: false, 16 - }, 17 - AllowedTypes: "image/jpeg,image/png,application/zip,application/gzip", 14 + Storage: &Storage{}, 15 + AllowedTypes: ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip", 18 16 MaxSize: 4, 19 17 MaxFiles: 5, 20 18 Enabled: true, 21 19 } 22 20 23 - func loadAttachmentFrom(rootCfg ConfigProvider) { 24 - sec := rootCfg.Section("attachment") 25 - storageType := sec.Key("STORAGE_TYPE").MustString("") 26 - 27 - Attachment.Storage = getStorage(rootCfg, "attachments", storageType, sec) 21 + func loadAttachmentFrom(rootCfg ConfigProvider) (err error) { 22 + sec, _ := rootCfg.GetSection("attachment") 23 + if sec == nil { 24 + Attachment.Storage, err = getStorage(rootCfg, "attachments", "", nil) 25 + return err 26 + } 28 27 29 28 Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") 30 29 Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) 31 30 Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5) 32 31 Attachment.Enabled = sec.Key("ENABLED").MustBool(true) 32 + 33 + Attachment.Storage, err = getStorage(rootCfg, "attachments", "", sec) 34 + return err 33 35 }
+133
modules/setting/attachment_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package setting 5 + 6 + import ( 7 + "testing" 8 + 9 + "github.com/stretchr/testify/assert" 10 + ) 11 + 12 + func Test_getStorageCustomType(t *testing.T) { 13 + iniStr := ` 14 + [attachment] 15 + STORAGE_TYPE = my_minio 16 + MINIO_BUCKET = gitea-attachment 17 + 18 + [storage.my_minio] 19 + STORAGE_TYPE = minio 20 + MINIO_ENDPOINT = my_minio:9000 21 + ` 22 + cfg, err := NewConfigProviderFromData(iniStr) 23 + assert.NoError(t, err) 24 + 25 + assert.NoError(t, loadAttachmentFrom(cfg)) 26 + 27 + assert.EqualValues(t, "minio", Attachment.Storage.Type) 28 + assert.EqualValues(t, "my_minio:9000", Attachment.Storage.MinioConfig.Endpoint) 29 + assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) 30 + assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) 31 + } 32 + 33 + func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) { 34 + iniStr := ` 35 + [attachment] 36 + STORAGE_TYPE = minio 37 + 38 + [storage.minio] 39 + MINIO_BUCKET = gitea-minio 40 + 41 + [storage] 42 + MINIO_BUCKET = gitea 43 + ` 44 + cfg, err := NewConfigProviderFromData(iniStr) 45 + assert.NoError(t, err) 46 + 47 + assert.NoError(t, loadAttachmentFrom(cfg)) 48 + 49 + assert.EqualValues(t, "minio", Attachment.Storage.Type) 50 + assert.EqualValues(t, "gitea-minio", Attachment.Storage.MinioConfig.Bucket) 51 + assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) 52 + } 53 + 54 + func Test_getStorageSpecificOverridesStorage(t *testing.T) { 55 + iniStr := ` 56 + [attachment] 57 + STORAGE_TYPE = minio 58 + MINIO_BUCKET = gitea-attachment 59 + 60 + [storage.attachments] 61 + MINIO_BUCKET = gitea 62 + 63 + [storage] 64 + STORAGE_TYPE = local 65 + ` 66 + cfg, err := NewConfigProviderFromData(iniStr) 67 + assert.NoError(t, err) 68 + 69 + assert.NoError(t, loadAttachmentFrom(cfg)) 70 + 71 + assert.EqualValues(t, "minio", Attachment.Storage.Type) 72 + assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) 73 + assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) 74 + } 75 + 76 + func Test_getStorageGetDefaults(t *testing.T) { 77 + cfg, err := NewConfigProviderFromData("") 78 + assert.NoError(t, err) 79 + 80 + assert.NoError(t, loadAttachmentFrom(cfg)) 81 + 82 + // default storage is local, so bucket is empty 83 + assert.EqualValues(t, "", Attachment.Storage.MinioConfig.Bucket) 84 + } 85 + 86 + func Test_getStorageInheritNameSectionType(t *testing.T) { 87 + iniStr := ` 88 + [storage.attachments] 89 + STORAGE_TYPE = minio 90 + ` 91 + cfg, err := NewConfigProviderFromData(iniStr) 92 + assert.NoError(t, err) 93 + 94 + assert.NoError(t, loadAttachmentFrom(cfg)) 95 + 96 + assert.EqualValues(t, "minio", Attachment.Storage.Type) 97 + } 98 + 99 + func Test_AttachmentStorage(t *testing.T) { 100 + iniStr := ` 101 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 102 + [storage] 103 + STORAGE_TYPE = minio 104 + MINIO_ENDPOINT = s3.my-domain.net 105 + MINIO_BUCKET = gitea 106 + MINIO_LOCATION = homenet 107 + MINIO_USE_SSL = true 108 + MINIO_ACCESS_KEY_ID = correct_key 109 + MINIO_SECRET_ACCESS_KEY = correct_key 110 + ` 111 + cfg, err := NewConfigProviderFromData(iniStr) 112 + assert.NoError(t, err) 113 + 114 + assert.NoError(t, loadAttachmentFrom(cfg)) 115 + storage := Attachment.Storage 116 + 117 + assert.EqualValues(t, "minio", storage.Type) 118 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 119 + } 120 + 121 + func Test_AttachmentStorage1(t *testing.T) { 122 + iniStr := ` 123 + [storage] 124 + STORAGE_TYPE = minio 125 + ` 126 + cfg, err := NewConfigProviderFromData(iniStr) 127 + assert.NoError(t, err) 128 + 129 + assert.NoError(t, loadAttachmentFrom(cfg)) 130 + assert.EqualValues(t, "minio", Attachment.Storage.Type) 131 + assert.EqualValues(t, "gitea", Attachment.Storage.MinioConfig.Bucket) 132 + assert.EqualValues(t, "attachments/", Attachment.Storage.MinioConfig.BasePath) 133 + }
+19
modules/setting/config_provider.go
··· 7 7 "fmt" 8 8 "os" 9 9 "path/filepath" 10 + "strconv" 10 11 "strings" 11 12 "time" 12 13 ··· 93 94 return def[0] 94 95 } 95 96 return "" 97 + } 98 + 99 + func ConfigSectionKeyBool(sec ConfigSection, key string, def ...bool) bool { 100 + k := ConfigSectionKey(sec, key) 101 + if k != nil && k.String() != "" { 102 + b, _ := strconv.ParseBool(k.String()) 103 + return b 104 + } 105 + if len(def) > 0 { 106 + return def[0] 107 + } 108 + return false 96 109 } 97 110 98 111 // ConfigInheritedKey works like ini.Section.Key(), but it always returns a new key instance, it is O(n) because NewKey is O(n) ··· 284 297 func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) { 285 298 if rootCfg.Section(oldSection).HasKey(oldKey) { 286 299 log.Error("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version) 300 + } 301 + } 302 + 303 + func deprecatedSettingFatal(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) { 304 + if rootCfg.Section(oldSection).HasKey(oldKey) { 305 + log.Fatal("Deprecated fallback `[%s]` `%s` present. Use `[%s]` `%s` instead. This fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version) 287 306 } 288 307 } 289 308
+28 -24
modules/setting/lfs.go
··· 5 5 6 6 import ( 7 7 "encoding/base64" 8 + "fmt" 8 9 "time" 9 10 10 11 "code.gitea.io/gitea/modules/generate" 11 - "code.gitea.io/gitea/modules/log" 12 12 ) 13 13 14 14 // LFS represents the configuration for Git LFS ··· 20 20 MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"` 21 21 LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"` 22 22 23 - Storage 23 + Storage *Storage 24 24 }{} 25 25 26 - func loadLFSFrom(rootCfg ConfigProvider) { 26 + func loadLFSFrom(rootCfg ConfigProvider) error { 27 27 sec := rootCfg.Section("server") 28 28 if err := sec.MapTo(&LFS); err != nil { 29 - log.Fatal("Failed to map LFS settings: %v", err) 29 + return fmt.Errorf("failed to map LFS settings: %v", err) 30 30 } 31 31 32 - lfsSec := rootCfg.Section("lfs") 33 - storageType := lfsSec.Key("STORAGE_TYPE").MustString("") 32 + lfsSec, _ := rootCfg.GetSection("lfs") 34 33 35 34 // Specifically default PATH to LFS_CONTENT_PATH 36 35 // DEPRECATED should not be removed because users maybe upgrade from lower version to the latest version 37 36 // if these are removed, the warning will not be shown 38 - deprecatedSetting(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0") 39 - lfsSec.Key("PATH").MustString(sec.Key("LFS_CONTENT_PATH").String()) 37 + deprecatedSettingFatal(rootCfg, "server", "LFS_CONTENT_PATH", "lfs", "PATH", "v1.19.0") 40 38 41 - LFS.Storage = getStorage(rootCfg, "lfs", storageType, lfsSec) 39 + var err error 40 + LFS.Storage, err = getStorage(rootCfg, "lfs", "", lfsSec) 41 + if err != nil { 42 + return err 43 + } 42 44 43 45 // Rest of LFS service settings 44 46 if LFS.LocksPagingNum == 0 { ··· 47 49 48 50 LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour) 49 51 50 - if LFS.StartServer { 51 - LFS.JWTSecretBytes = make([]byte, 32) 52 - n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64)) 52 + if !LFS.StartServer { 53 + return nil 54 + } 53 55 54 - if err != nil || n != 32 { 55 - LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64() 56 - if err != nil { 57 - log.Fatal("Error generating JWT Secret for custom config: %v", err) 58 - return 59 - } 56 + LFS.JWTSecretBytes = make([]byte, 32) 57 + n, err := base64.RawURLEncoding.Decode(LFS.JWTSecretBytes, []byte(LFS.JWTSecretBase64)) 60 58 61 - // Save secret 62 - sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64) 63 - if err := rootCfg.Save(); err != nil { 64 - log.Fatal("Error saving JWT Secret for custom config: %v", err) 65 - return 66 - } 59 + if err != nil || n != 32 { 60 + LFS.JWTSecretBase64, err = generate.NewJwtSecretBase64() 61 + if err != nil { 62 + return fmt.Errorf("Error generating JWT Secret for custom config: %v", err) 63 + } 64 + 65 + // Save secret 66 + sec.Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64) 67 + if err := rootCfg.Save(); err != nil { 68 + return fmt.Errorf("Error saving JWT Secret for custom config: %v", err) 67 69 } 68 70 } 71 + 72 + return nil 69 73 }
+77
modules/setting/lfs_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package setting 5 + 6 + import ( 7 + "testing" 8 + 9 + "github.com/stretchr/testify/assert" 10 + ) 11 + 12 + func Test_getStorageInheritNameSectionTypeForLFS(t *testing.T) { 13 + iniStr := ` 14 + [storage] 15 + STORAGE_TYPE = minio 16 + ` 17 + cfg, err := NewConfigProviderFromData(iniStr) 18 + assert.NoError(t, err) 19 + assert.NoError(t, loadLFSFrom(cfg)) 20 + 21 + assert.EqualValues(t, "minio", LFS.Storage.Type) 22 + assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) 23 + 24 + iniStr = ` 25 + [storage.lfs] 26 + STORAGE_TYPE = minio 27 + ` 28 + cfg, err = NewConfigProviderFromData(iniStr) 29 + assert.NoError(t, err) 30 + assert.NoError(t, loadLFSFrom(cfg)) 31 + 32 + assert.EqualValues(t, "minio", LFS.Storage.Type) 33 + assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) 34 + 35 + iniStr = ` 36 + [lfs] 37 + STORAGE_TYPE = my_minio 38 + 39 + [storage.my_minio] 40 + STORAGE_TYPE = minio 41 + ` 42 + cfg, err = NewConfigProviderFromData(iniStr) 43 + assert.NoError(t, err) 44 + assert.NoError(t, loadLFSFrom(cfg)) 45 + 46 + assert.EqualValues(t, "minio", LFS.Storage.Type) 47 + assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) 48 + 49 + iniStr = ` 50 + [lfs] 51 + STORAGE_TYPE = my_minio 52 + MINIO_BASE_PATH = my_lfs/ 53 + 54 + [storage.my_minio] 55 + STORAGE_TYPE = minio 56 + ` 57 + cfg, err = NewConfigProviderFromData(iniStr) 58 + assert.NoError(t, err) 59 + assert.NoError(t, loadLFSFrom(cfg)) 60 + 61 + assert.EqualValues(t, "minio", LFS.Storage.Type) 62 + assert.EqualValues(t, "my_lfs/", LFS.Storage.MinioConfig.BasePath) 63 + } 64 + 65 + func Test_LFSStorage1(t *testing.T) { 66 + iniStr := ` 67 + [storage] 68 + STORAGE_TYPE = minio 69 + ` 70 + cfg, err := NewConfigProviderFromData(iniStr) 71 + assert.NoError(t, err) 72 + 73 + assert.NoError(t, loadLFSFrom(cfg)) 74 + assert.EqualValues(t, "minio", LFS.Storage.Type) 75 + assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket) 76 + assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath) 77 + }
+17 -9
modules/setting/packages.go
··· 4 4 package setting 5 5 6 6 import ( 7 + "fmt" 7 8 "math" 8 9 "net/url" 9 10 "os" 10 11 "path/filepath" 11 - 12 - "code.gitea.io/gitea/modules/log" 13 12 14 13 "github.com/dustin/go-humanize" 15 14 ) ··· 17 16 // Package registry settings 18 17 var ( 19 18 Packages = struct { 20 - Storage 19 + Storage *Storage 21 20 Enabled bool 22 21 ChunkedUploadPath string 23 22 RegistryHost string ··· 51 50 } 52 51 ) 53 52 54 - func loadPackagesFrom(rootCfg ConfigProvider) { 55 - sec := rootCfg.Section("packages") 56 - if err := sec.MapTo(&Packages); err != nil { 57 - log.Fatal("Failed to map Packages settings: %v", err) 53 + func loadPackagesFrom(rootCfg ConfigProvider) (err error) { 54 + sec, _ := rootCfg.GetSection("packages") 55 + if sec == nil { 56 + Packages.Storage, err = getStorage(rootCfg, "packages", "", nil) 57 + return err 58 58 } 59 59 60 - Packages.Storage = getStorage(rootCfg, "packages", "", nil) 60 + if err = sec.MapTo(&Packages); err != nil { 61 + return fmt.Errorf("failed to map Packages settings: %v", err) 62 + } 63 + 64 + Packages.Storage, err = getStorage(rootCfg, "packages", "", sec) 65 + if err != nil { 66 + return err 67 + } 61 68 62 69 appURL, _ := url.Parse(AppURL) 63 70 Packages.RegistryHost = appURL.Host ··· 68 75 } 69 76 70 77 if err := os.MkdirAll(Packages.ChunkedUploadPath, os.ModePerm); err != nil { 71 - log.Error("Unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err) 78 + return fmt.Errorf("unable to create chunked upload directory: %s (%v)", Packages.ChunkedUploadPath, err) 72 79 } 73 80 74 81 Packages.LimitTotalOwnerSize = mustBytes(sec, "LIMIT_TOTAL_OWNER_SIZE") ··· 93 100 Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS") 94 101 Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT") 95 102 Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT") 103 + return nil 96 104 } 97 105 98 106 func mustBytes(section ConfigSection, key string) int64 {
+167
modules/setting/packages_test.go
··· 29 29 assert.EqualValues(t, 1782579, test("1.7mib")) 30 30 assert.EqualValues(t, -1, test("1 yib")) // too large 31 31 } 32 + 33 + func Test_getStorageInheritNameSectionTypeForPackages(t *testing.T) { 34 + // packages storage inherits from storage if nothing configured 35 + iniStr := ` 36 + [storage] 37 + STORAGE_TYPE = minio 38 + ` 39 + cfg, err := NewConfigProviderFromData(iniStr) 40 + assert.NoError(t, err) 41 + assert.NoError(t, loadPackagesFrom(cfg)) 42 + 43 + assert.EqualValues(t, "minio", Packages.Storage.Type) 44 + assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) 45 + 46 + // we can also configure packages storage directly 47 + iniStr = ` 48 + [storage.packages] 49 + STORAGE_TYPE = minio 50 + ` 51 + cfg, err = NewConfigProviderFromData(iniStr) 52 + assert.NoError(t, err) 53 + assert.NoError(t, loadPackagesFrom(cfg)) 54 + 55 + assert.EqualValues(t, "minio", Packages.Storage.Type) 56 + assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) 57 + 58 + // or we can indicate the storage type in the packages section 59 + iniStr = ` 60 + [packages] 61 + STORAGE_TYPE = my_minio 62 + 63 + [storage.my_minio] 64 + STORAGE_TYPE = minio 65 + ` 66 + cfg, err = NewConfigProviderFromData(iniStr) 67 + assert.NoError(t, err) 68 + assert.NoError(t, loadPackagesFrom(cfg)) 69 + 70 + assert.EqualValues(t, "minio", Packages.Storage.Type) 71 + assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) 72 + 73 + // or we can indicate the storage type and minio base path in the packages section 74 + iniStr = ` 75 + [packages] 76 + STORAGE_TYPE = my_minio 77 + MINIO_BASE_PATH = my_packages/ 78 + 79 + [storage.my_minio] 80 + STORAGE_TYPE = minio 81 + ` 82 + cfg, err = NewConfigProviderFromData(iniStr) 83 + assert.NoError(t, err) 84 + assert.NoError(t, loadPackagesFrom(cfg)) 85 + 86 + assert.EqualValues(t, "minio", Packages.Storage.Type) 87 + assert.EqualValues(t, "my_packages/", Packages.Storage.MinioConfig.BasePath) 88 + } 89 + 90 + func Test_PackageStorage1(t *testing.T) { 91 + iniStr := ` 92 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 93 + [packages] 94 + MINIO_BASE_PATH = packages/ 95 + SERVE_DIRECT = true 96 + [storage] 97 + STORAGE_TYPE = minio 98 + MINIO_ENDPOINT = s3.my-domain.net 99 + MINIO_BUCKET = gitea 100 + MINIO_LOCATION = homenet 101 + MINIO_USE_SSL = true 102 + MINIO_ACCESS_KEY_ID = correct_key 103 + MINIO_SECRET_ACCESS_KEY = correct_key 104 + ` 105 + cfg, err := NewConfigProviderFromData(iniStr) 106 + assert.NoError(t, err) 107 + 108 + assert.NoError(t, loadPackagesFrom(cfg)) 109 + storage := Packages.Storage 110 + 111 + assert.EqualValues(t, "minio", storage.Type) 112 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 113 + assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) 114 + assert.True(t, storage.MinioConfig.ServeDirect) 115 + } 116 + 117 + func Test_PackageStorage2(t *testing.T) { 118 + iniStr := ` 119 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 120 + [storage.packages] 121 + MINIO_BASE_PATH = packages/ 122 + SERVE_DIRECT = true 123 + [storage] 124 + STORAGE_TYPE = minio 125 + MINIO_ENDPOINT = s3.my-domain.net 126 + MINIO_BUCKET = gitea 127 + MINIO_LOCATION = homenet 128 + MINIO_USE_SSL = true 129 + MINIO_ACCESS_KEY_ID = correct_key 130 + MINIO_SECRET_ACCESS_KEY = correct_key 131 + ` 132 + cfg, err := NewConfigProviderFromData(iniStr) 133 + assert.NoError(t, err) 134 + 135 + assert.NoError(t, loadPackagesFrom(cfg)) 136 + storage := Packages.Storage 137 + 138 + assert.EqualValues(t, "minio", storage.Type) 139 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 140 + assert.EqualValues(t, "packages/", storage.MinioConfig.BasePath) 141 + assert.True(t, storage.MinioConfig.ServeDirect) 142 + } 143 + 144 + func Test_PackageStorage3(t *testing.T) { 145 + iniStr := ` 146 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 147 + [packages] 148 + STORAGE_TYPE = my_cfg 149 + MINIO_BASE_PATH = my_packages/ 150 + SERVE_DIRECT = true 151 + [storage.my_cfg] 152 + STORAGE_TYPE = minio 153 + MINIO_ENDPOINT = s3.my-domain.net 154 + MINIO_BUCKET = gitea 155 + MINIO_LOCATION = homenet 156 + MINIO_USE_SSL = true 157 + MINIO_ACCESS_KEY_ID = correct_key 158 + MINIO_SECRET_ACCESS_KEY = correct_key 159 + ` 160 + cfg, err := NewConfigProviderFromData(iniStr) 161 + assert.NoError(t, err) 162 + 163 + assert.NoError(t, loadPackagesFrom(cfg)) 164 + storage := Packages.Storage 165 + 166 + assert.EqualValues(t, "minio", storage.Type) 167 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 168 + assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) 169 + assert.True(t, storage.MinioConfig.ServeDirect) 170 + } 171 + 172 + func Test_PackageStorage4(t *testing.T) { 173 + iniStr := ` 174 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 175 + [storage.packages] 176 + STORAGE_TYPE = my_cfg 177 + MINIO_BASE_PATH = my_packages/ 178 + SERVE_DIRECT = true 179 + [storage.my_cfg] 180 + STORAGE_TYPE = minio 181 + MINIO_ENDPOINT = s3.my-domain.net 182 + MINIO_BUCKET = gitea 183 + MINIO_LOCATION = homenet 184 + MINIO_USE_SSL = true 185 + MINIO_ACCESS_KEY_ID = correct_key 186 + MINIO_SECRET_ACCESS_KEY = correct_key 187 + ` 188 + cfg, err := NewConfigProviderFromData(iniStr) 189 + assert.NoError(t, err) 190 + 191 + assert.NoError(t, loadPackagesFrom(cfg)) 192 + storage := Packages.Storage 193 + 194 + assert.EqualValues(t, "minio", storage.Type) 195 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 196 + assert.EqualValues(t, "my_packages/", storage.MinioConfig.BasePath) 197 + assert.True(t, storage.MinioConfig.ServeDirect) 198 + }
+19 -11
modules/setting/picture.go
··· 7 7 8 8 var ( 9 9 Avatar = struct { 10 - Storage 10 + Storage *Storage 11 11 12 12 MaxWidth int 13 13 MaxHeight int ··· 27 27 EnableFederatedAvatar bool // Depreciated: migrated to database 28 28 29 29 RepoAvatar = struct { 30 - Storage 30 + Storage *Storage 31 31 32 32 Fallback string 33 33 FallbackImage string 34 34 }{} 35 35 ) 36 36 37 - func loadPictureFrom(rootCfg ConfigProvider) { 37 + func loadAvatarsFrom(rootCfg ConfigProvider) error { 38 38 sec := rootCfg.Section("picture") 39 39 40 40 avatarSec := rootCfg.Section("avatar") 41 41 storageType := sec.Key("AVATAR_STORAGE_TYPE").MustString("") 42 42 // Specifically default PATH to AVATAR_UPLOAD_PATH 43 - avatarSec.Key("PATH").MustString( 44 - sec.Key("AVATAR_UPLOAD_PATH").String()) 43 + avatarSec.Key("PATH").MustString(sec.Key("AVATAR_UPLOAD_PATH").String()) 45 44 46 - Avatar.Storage = getStorage(rootCfg, "avatars", storageType, avatarSec) 45 + var err error 46 + Avatar.Storage, err = getStorage(rootCfg, "avatars", storageType, avatarSec) 47 + if err != nil { 48 + return err 49 + } 47 50 48 51 Avatar.MaxWidth = sec.Key("AVATAR_MAX_WIDTH").MustInt(4096) 49 52 Avatar.MaxHeight = sec.Key("AVATAR_MAX_HEIGHT").MustInt(4096) ··· 67 70 EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar)) 68 71 deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR") 69 72 70 - loadRepoAvatarFrom(rootCfg) 73 + return nil 71 74 } 72 75 73 76 func GetDefaultDisableGravatar() bool { ··· 85 88 return v 86 89 } 87 90 88 - func loadRepoAvatarFrom(rootCfg ConfigProvider) { 91 + func loadRepoAvatarFrom(rootCfg ConfigProvider) error { 89 92 sec := rootCfg.Section("picture") 90 93 91 94 repoAvatarSec := rootCfg.Section("repo-avatar") 92 95 storageType := sec.Key("REPOSITORY_AVATAR_STORAGE_TYPE").MustString("") 93 96 // Specifically default PATH to AVATAR_UPLOAD_PATH 94 - repoAvatarSec.Key("PATH").MustString( 95 - sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) 97 + repoAvatarSec.Key("PATH").MustString(sec.Key("REPOSITORY_AVATAR_UPLOAD_PATH").String()) 96 98 97 - RepoAvatar.Storage = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec) 99 + var err error 100 + RepoAvatar.Storage, err = getStorage(rootCfg, "repo-avatars", storageType, repoAvatarSec) 101 + if err != nil { 102 + return err 103 + } 98 104 99 105 RepoAvatar.Fallback = sec.Key("REPOSITORY_AVATAR_FALLBACK").MustString("none") 100 106 RepoAvatar.FallbackImage = sec.Key("REPOSITORY_AVATAR_FALLBACK_IMAGE").MustString(AppSubURL + "/assets/img/repo_default.png") 107 + 108 + return nil 101 109 }
+3 -5
modules/setting/repository.go
··· 265 265 } 266 266 RepoRootPath string 267 267 ScriptType = "bash" 268 - 269 - RepoArchive = struct { 270 - Storage 271 - }{} 272 268 ) 273 269 274 270 func loadRepositoryFrom(rootCfg ConfigProvider) { ··· 359 355 Repository.Upload.TempPath = path.Join(AppWorkPath, Repository.Upload.TempPath) 360 356 } 361 357 362 - RepoArchive.Storage = getStorage(rootCfg, "repo-archive", "", nil) 358 + if err := loadRepoArchiveFrom(rootCfg); err != nil { 359 + log.Fatal("loadRepoArchiveFrom: %v", err) 360 + } 363 361 }
+25
modules/setting/repository_archive.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package setting 5 + 6 + import "fmt" 7 + 8 + var RepoArchive = struct { 9 + Storage *Storage 10 + }{} 11 + 12 + func loadRepoArchiveFrom(rootCfg ConfigProvider) (err error) { 13 + sec, _ := rootCfg.GetSection("repo-archive") 14 + if sec == nil { 15 + RepoArchive.Storage, err = getStorage(rootCfg, "repo-archive", "", nil) 16 + return err 17 + } 18 + 19 + if err := sec.MapTo(&RepoArchive); err != nil { 20 + return fmt.Errorf("mapto repoarchive failed: %v", err) 21 + } 22 + 23 + RepoArchive.Storage, err = getStorage(rootCfg, "repo-archive", "", sec) 24 + return err 25 + }
+111
modules/setting/repository_archive_test.go
··· 1 + // Copyright 2023 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package setting 5 + 6 + import ( 7 + "testing" 8 + 9 + "github.com/stretchr/testify/assert" 10 + ) 11 + 12 + func Test_getStorageInheritNameSectionTypeForRepoArchive(t *testing.T) { 13 + // packages storage inherits from storage if nothing configured 14 + iniStr := ` 15 + [storage] 16 + STORAGE_TYPE = minio 17 + ` 18 + cfg, err := NewConfigProviderFromData(iniStr) 19 + assert.NoError(t, err) 20 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 21 + 22 + assert.EqualValues(t, "minio", RepoArchive.Storage.Type) 23 + assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) 24 + 25 + // we can also configure packages storage directly 26 + iniStr = ` 27 + [storage.repo-archive] 28 + STORAGE_TYPE = minio 29 + ` 30 + cfg, err = NewConfigProviderFromData(iniStr) 31 + assert.NoError(t, err) 32 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 33 + 34 + assert.EqualValues(t, "minio", RepoArchive.Storage.Type) 35 + assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) 36 + 37 + // or we can indicate the storage type in the packages section 38 + iniStr = ` 39 + [repo-archive] 40 + STORAGE_TYPE = my_minio 41 + 42 + [storage.my_minio] 43 + STORAGE_TYPE = minio 44 + ` 45 + cfg, err = NewConfigProviderFromData(iniStr) 46 + assert.NoError(t, err) 47 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 48 + 49 + assert.EqualValues(t, "minio", RepoArchive.Storage.Type) 50 + assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) 51 + 52 + // or we can indicate the storage type and minio base path in the packages section 53 + iniStr = ` 54 + [repo-archive] 55 + STORAGE_TYPE = my_minio 56 + MINIO_BASE_PATH = my_archive/ 57 + 58 + [storage.my_minio] 59 + STORAGE_TYPE = minio 60 + ` 61 + cfg, err = NewConfigProviderFromData(iniStr) 62 + assert.NoError(t, err) 63 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 64 + 65 + assert.EqualValues(t, "minio", RepoArchive.Storage.Type) 66 + assert.EqualValues(t, "my_archive/", RepoArchive.Storage.MinioConfig.BasePath) 67 + } 68 + 69 + func Test_RepoArchiveStorage(t *testing.T) { 70 + iniStr := ` 71 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72 + [storage] 73 + STORAGE_TYPE = minio 74 + MINIO_ENDPOINT = s3.my-domain.net 75 + MINIO_BUCKET = gitea 76 + MINIO_LOCATION = homenet 77 + MINIO_USE_SSL = true 78 + MINIO_ACCESS_KEY_ID = correct_key 79 + MINIO_SECRET_ACCESS_KEY = correct_key 80 + ` 81 + cfg, err := NewConfigProviderFromData(iniStr) 82 + assert.NoError(t, err) 83 + 84 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 85 + storage := RepoArchive.Storage 86 + 87 + assert.EqualValues(t, "minio", storage.Type) 88 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 89 + 90 + iniStr = ` 91 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 92 + [storage.repo-archive] 93 + STORAGE_TYPE = s3 94 + [storage.s3] 95 + STORAGE_TYPE = minio 96 + MINIO_ENDPOINT = s3.my-domain.net 97 + MINIO_BUCKET = gitea 98 + MINIO_LOCATION = homenet 99 + MINIO_USE_SSL = true 100 + MINIO_ACCESS_KEY_ID = correct_key 101 + MINIO_SECRET_ACCESS_KEY = correct_key 102 + ` 103 + cfg, err = NewConfigProviderFromData(iniStr) 104 + assert.NoError(t, err) 105 + 106 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 107 + storage = RepoArchive.Storage 108 + 109 + assert.EqualValues(t, "minio", storage.Type) 110 + assert.EqualValues(t, "gitea", storage.MinioConfig.Bucket) 111 + }
+25 -9
modules/setting/setting.go
··· 203 203 var err error 204 204 CfgProvider, err = NewConfigProviderFromFile(opts) 205 205 if err != nil { 206 - log.Fatal("Init[%v]: %v", opts, err) 206 + log.Fatal("newConfigProviderFromFile[%v]: %v", opts, err) 207 207 } 208 208 if !opts.DisableLoadCommonSettings { 209 - loadCommonSettingsFrom(CfgProvider) 209 + if err := loadCommonSettingsFrom(CfgProvider); err != nil { 210 + log.Fatal("loadCommonSettingsFrom[%v]: %v", opts, err) 211 + } 210 212 } 211 213 } 212 214 213 215 // loadCommonSettingsFrom loads common configurations from a configuration provider. 214 - func loadCommonSettingsFrom(cfg ConfigProvider) { 215 - // WARNING: don't change the sequence except you know what you are doing. 216 + func loadCommonSettingsFrom(cfg ConfigProvider) error { 217 + // WARNNING: don't change the sequence except you know what you are doing. 216 218 loadRunModeFrom(cfg) 217 219 loadLogGlobalFrom(cfg) 218 220 loadServerFrom(cfg) ··· 222 224 223 225 loadOAuth2From(cfg) 224 226 loadSecurityFrom(cfg) 225 - loadAttachmentFrom(cfg) 226 - loadLFSFrom(cfg) 227 + if err := loadAttachmentFrom(cfg); err != nil { 228 + return err 229 + } 230 + if err := loadLFSFrom(cfg); err != nil { 231 + return err 232 + } 227 233 loadTimeFrom(cfg) 228 234 loadRepositoryFrom(cfg) 229 - loadPictureFrom(cfg) 230 - loadPackagesFrom(cfg) 231 - loadActionsFrom(cfg) 235 + if err := loadAvatarsFrom(cfg); err != nil { 236 + return err 237 + } 238 + if err := loadRepoAvatarFrom(cfg); err != nil { 239 + return err 240 + } 241 + if err := loadPackagesFrom(cfg); err != nil { 242 + return err 243 + } 244 + if err := loadActionsFrom(cfg); err != nil { 245 + return err 246 + } 232 247 loadUIFrom(cfg) 233 248 loadAdminFrom(cfg) 234 249 loadAPIFrom(cfg) ··· 239 254 loadMirrorFrom(cfg) 240 255 loadMarkupFrom(cfg) 241 256 loadOtherFrom(cfg) 257 + return nil 242 258 } 243 259 244 260 func loadRunModeFrom(rootCfg ConfigProvider) {
+152 -57
modules/setting/storage.go
··· 4 4 package setting 5 5 6 6 import ( 7 + "errors" 8 + "fmt" 7 9 "path/filepath" 8 - "reflect" 9 10 ) 10 11 12 + // StorageType is a type of Storage 13 + type StorageType string 14 + 15 + const ( 16 + // LocalStorageType is the type descriptor for local storage 17 + LocalStorageType StorageType = "local" 18 + // MinioStorageType is the type descriptor for minio storage 19 + MinioStorageType StorageType = "minio" 20 + ) 21 + 22 + var storageTypes = []StorageType{ 23 + LocalStorageType, 24 + MinioStorageType, 25 + } 26 + 27 + // IsValidStorageType returns true if the given storage type is valid 28 + func IsValidStorageType(storageType StorageType) bool { 29 + for _, t := range storageTypes { 30 + if t == storageType { 31 + return true 32 + } 33 + } 34 + return false 35 + } 36 + 37 + // MinioStorageConfig represents the configuration for a minio storage 38 + type MinioStorageConfig struct { 39 + Endpoint string `ini:"MINIO_ENDPOINT" json:",omitempty"` 40 + AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID" json:",omitempty"` 41 + SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty"` 42 + Bucket string `ini:"MINIO_BUCKET" json:",omitempty"` 43 + Location string `ini:"MINIO_LOCATION" json:",omitempty"` 44 + BasePath string `ini:"MINIO_BASE_PATH" json:",omitempty"` 45 + UseSSL bool `ini:"MINIO_USE_SSL"` 46 + InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"` 47 + ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty"` 48 + ServeDirect bool `ini:"SERVE_DIRECT"` 49 + } 50 + 11 51 // Storage represents configuration of storages 12 52 type Storage struct { 13 - Type string 14 - Path string 15 - Section ConfigSection 16 - ServeDirect bool 53 + Type StorageType // local or minio 54 + Path string `json:",omitempty"` // for local type 55 + TemporaryPath string `json:",omitempty"` 56 + MinioConfig MinioStorageConfig // for minio type 17 57 } 18 58 19 - // MapTo implements the Mappable interface 20 - func (s *Storage) MapTo(v interface{}) error { 21 - pathValue := reflect.ValueOf(v).Elem().FieldByName("Path") 22 - if pathValue.IsValid() && pathValue.Kind() == reflect.String { 23 - pathValue.SetString(s.Path) 59 + func (storage *Storage) ToShadowCopy() Storage { 60 + shadowStorage := *storage 61 + if shadowStorage.MinioConfig.AccessKeyID != "" { 62 + shadowStorage.MinioConfig.AccessKeyID = "******" 24 63 } 25 - if s.Section != nil { 26 - return s.Section.MapTo(v) 64 + if shadowStorage.MinioConfig.SecretAccessKey != "" { 65 + shadowStorage.MinioConfig.SecretAccessKey = "******" 27 66 } 28 - return nil 67 + return shadowStorage 29 68 } 30 69 31 - func getStorage(rootCfg ConfigProvider, name, typ string, targetSec ConfigSection) Storage { 32 - const sectionName = "storage" 33 - sec := rootCfg.Section(sectionName) 70 + const storageSectionName = "storage" 34 71 72 + func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection { 73 + storageSec := rootCfg.Section(storageSectionName) 35 74 // Global Defaults 36 - sec.Key("MINIO_ENDPOINT").MustString("localhost:9000") 37 - sec.Key("MINIO_ACCESS_KEY_ID").MustString("") 38 - sec.Key("MINIO_SECRET_ACCESS_KEY").MustString("") 39 - sec.Key("MINIO_BUCKET").MustString("gitea") 40 - sec.Key("MINIO_LOCATION").MustString("us-east-1") 41 - sec.Key("MINIO_USE_SSL").MustBool(false) 42 - sec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false) 43 - sec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default") 75 + storageSec.Key("STORAGE_TYPE").MustString("local") 76 + storageSec.Key("MINIO_ENDPOINT").MustString("localhost:9000") 77 + storageSec.Key("MINIO_ACCESS_KEY_ID").MustString("") 78 + storageSec.Key("MINIO_SECRET_ACCESS_KEY").MustString("") 79 + storageSec.Key("MINIO_BUCKET").MustString("gitea") 80 + storageSec.Key("MINIO_LOCATION").MustString("us-east-1") 81 + storageSec.Key("MINIO_USE_SSL").MustBool(false) 82 + storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false) 83 + storageSec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default") 84 + return storageSec 85 + } 86 + 87 + func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*Storage, error) { 88 + if name == "" { 89 + return nil, errors.New("no name for storage") 90 + } 91 + 92 + var targetSec ConfigSection 93 + if typ != "" { 94 + var err error 95 + targetSec, err = rootCfg.GetSection(storageSectionName + "." + typ) 96 + if err != nil { 97 + if !IsValidStorageType(StorageType(typ)) { 98 + return nil, fmt.Errorf("get section via storage type %q failed: %v", typ, err) 99 + } 100 + } 101 + if targetSec != nil { 102 + targetType := targetSec.Key("STORAGE_TYPE").String() 103 + if targetType == "" { 104 + if !IsValidStorageType(StorageType(typ)) { 105 + return nil, fmt.Errorf("unknow storage type %q", typ) 106 + } 107 + targetSec.Key("STORAGE_TYPE").SetValue(typ) 108 + } else if !IsValidStorageType(StorageType(targetType)) { 109 + return nil, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ) 110 + } 111 + } 112 + } 44 113 114 + storageNameSec, _ := rootCfg.GetSection(storageSectionName + "." + name) 115 + 116 + if targetSec == nil { 117 + targetSec = sec 118 + } 45 119 if targetSec == nil { 46 - targetSec, _ = rootCfg.NewSection(name) 120 + targetSec = storageNameSec 47 121 } 48 - 49 - var storage Storage 50 - storage.Section = targetSec 51 - storage.Type = typ 122 + if targetSec == nil { 123 + targetSec = getDefaultStorageSection(rootCfg) 124 + } else { 125 + targetType := targetSec.Key("STORAGE_TYPE").String() 126 + switch { 127 + case targetType == "": 128 + if targetSec.Key("PATH").String() == "" { 129 + targetSec = getDefaultStorageSection(rootCfg) 130 + } else { 131 + targetSec.Key("STORAGE_TYPE").SetValue("local") 132 + } 133 + default: 134 + newTargetSec, _ := rootCfg.GetSection(storageSectionName + "." + targetType) 135 + if newTargetSec == nil { 136 + if !IsValidStorageType(StorageType(targetType)) { 137 + return nil, fmt.Errorf("invalid storage section %s.%q", storageSectionName, targetType) 138 + } 139 + } else { 140 + targetSec = newTargetSec 141 + if IsValidStorageType(StorageType(targetType)) { 142 + tp := targetSec.Key("STORAGE_TYPE").String() 143 + if tp == "" { 144 + targetSec.Key("STORAGE_TYPE").SetValue(targetType) 145 + } 146 + } 147 + } 148 + } 149 + } 52 150 53 - overrides := make([]ConfigSection, 0, 3) 54 - nameSec, err := rootCfg.GetSection(sectionName + "." + name) 55 - if err == nil { 56 - overrides = append(overrides, nameSec) 151 + targetType := targetSec.Key("STORAGE_TYPE").String() 152 + if !IsValidStorageType(StorageType(targetType)) { 153 + return nil, fmt.Errorf("invalid storage type %q", targetType) 57 154 } 58 155 59 - typeSec, err := rootCfg.GetSection(sectionName + "." + typ) 60 - if err == nil { 61 - overrides = append(overrides, typeSec) 62 - nextType := typeSec.Key("STORAGE_TYPE").String() 63 - if len(nextType) > 0 { 64 - storage.Type = nextType // Support custom STORAGE_TYPE 156 + var storage Storage 157 + storage.Type = StorageType(targetType) 158 + 159 + switch targetType { 160 + case string(LocalStorageType): 161 + storage.Path = ConfigSectionKeyString(targetSec, "PATH", filepath.Join(AppDataPath, name)) 162 + if !filepath.IsAbs(storage.Path) { 163 + storage.Path = filepath.Join(AppWorkPath, storage.Path) 65 164 } 66 - } 67 - overrides = append(overrides, sec) 165 + case string(MinioStorageType): 166 + storage.MinioConfig.BasePath = name + "/" 68 167 69 - for _, override := range overrides { 70 - for _, key := range override.Keys() { 71 - if !targetSec.HasKey(key.Name()) { 72 - _, _ = targetSec.NewKey(key.Name(), key.Value()) 73 - } 168 + if err := targetSec.MapTo(&storage.MinioConfig); err != nil { 169 + return nil, fmt.Errorf("map minio config failed: %v", err) 74 170 } 75 - if len(storage.Type) == 0 { 76 - storage.Type = override.Key("STORAGE_TYPE").String() 171 + // extra config section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH to override the targetsec 172 + extraConfigSec := sec 173 + if extraConfigSec == nil { 174 + extraConfigSec = storageNameSec 77 175 } 78 - } 79 - storage.ServeDirect = storage.Section.Key("SERVE_DIRECT").MustBool(false) 80 176 81 - // Specific defaults 82 - storage.Path = storage.Section.Key("PATH").MustString(filepath.Join(AppDataPath, name)) 83 - if !filepath.IsAbs(storage.Path) { 84 - storage.Path = filepath.Join(AppWorkPath, storage.Path) 85 - storage.Section.Key("PATH").SetValue(storage.Path) 177 + if extraConfigSec != nil { 178 + storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(extraConfigSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) 179 + storage.MinioConfig.BasePath = ConfigSectionKeyString(extraConfigSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath) 180 + storage.MinioConfig.Bucket = ConfigSectionKeyString(extraConfigSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) 181 + } 86 182 } 87 - storage.Section.Key("MINIO_BASE_PATH").MustString(name + "/") 88 183 89 - return storage 184 + return &storage, nil 90 185 }
+36 -148
modules/setting/storage_test.go
··· 9 9 "github.com/stretchr/testify/assert" 10 10 ) 11 11 12 - func Test_getStorageCustomType(t *testing.T) { 13 - iniStr := ` 14 - [attachment] 15 - STORAGE_TYPE = my_minio 16 - MINIO_BUCKET = gitea-attachment 17 - 18 - [storage.my_minio] 19 - STORAGE_TYPE = minio 20 - MINIO_ENDPOINT = my_minio:9000 21 - ` 22 - cfg, err := NewConfigProviderFromData(iniStr) 23 - assert.NoError(t, err) 24 - 25 - sec := cfg.Section("attachment") 26 - storageType := sec.Key("STORAGE_TYPE").MustString("") 27 - storage := getStorage(cfg, "attachments", storageType, sec) 28 - 29 - assert.EqualValues(t, "minio", storage.Type) 30 - assert.EqualValues(t, "my_minio:9000", storage.Section.Key("MINIO_ENDPOINT").String()) 31 - assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) 32 - } 33 - 34 - func Test_getStorageNameSectionOverridesTypeSection(t *testing.T) { 35 - iniStr := ` 36 - [attachment] 37 - STORAGE_TYPE = minio 38 - 39 - [storage.attachments] 40 - MINIO_BUCKET = gitea-attachment 41 - 42 - [storage.minio] 43 - MINIO_BUCKET = gitea 44 - ` 45 - cfg, err := NewConfigProviderFromData(iniStr) 46 - assert.NoError(t, err) 47 - 48 - sec := cfg.Section("attachment") 49 - storageType := sec.Key("STORAGE_TYPE").MustString("") 50 - storage := getStorage(cfg, "attachments", storageType, sec) 51 - 52 - assert.EqualValues(t, "minio", storage.Type) 53 - assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) 54 - } 55 - 56 - func Test_getStorageTypeSectionOverridesStorageSection(t *testing.T) { 57 - iniStr := ` 58 - [attachment] 59 - STORAGE_TYPE = minio 60 - 61 - [storage.minio] 62 - MINIO_BUCKET = gitea-minio 63 - 64 - [storage] 65 - MINIO_BUCKET = gitea 66 - ` 67 - cfg, err := NewConfigProviderFromData(iniStr) 68 - assert.NoError(t, err) 69 - 70 - sec := cfg.Section("attachment") 71 - storageType := sec.Key("STORAGE_TYPE").MustString("") 72 - storage := getStorage(cfg, "attachments", storageType, sec) 73 - 74 - assert.EqualValues(t, "minio", storage.Type) 75 - assert.EqualValues(t, "gitea-minio", storage.Section.Key("MINIO_BUCKET").String()) 76 - } 77 - 78 - func Test_getStorageSpecificOverridesStorage(t *testing.T) { 79 - iniStr := ` 80 - [attachment] 81 - STORAGE_TYPE = minio 82 - MINIO_BUCKET = gitea-attachment 83 - 84 - [storage.attachments] 85 - MINIO_BUCKET = gitea 86 - 87 - [storage] 88 - STORAGE_TYPE = local 89 - ` 90 - cfg, err := NewConfigProviderFromData(iniStr) 91 - assert.NoError(t, err) 92 - 93 - sec := cfg.Section("attachment") 94 - storageType := sec.Key("STORAGE_TYPE").MustString("") 95 - storage := getStorage(cfg, "attachments", storageType, sec) 96 - 97 - assert.EqualValues(t, "minio", storage.Type) 98 - assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) 99 - } 100 - 101 - func Test_getStorageGetDefaults(t *testing.T) { 102 - cfg, err := NewConfigProviderFromData("") 103 - assert.NoError(t, err) 104 - 105 - sec := cfg.Section("attachment") 106 - storageType := sec.Key("STORAGE_TYPE").MustString("") 107 - storage := getStorage(cfg, "attachments", storageType, sec) 108 - 109 - assert.EqualValues(t, "gitea", storage.Section.Key("MINIO_BUCKET").String()) 110 - } 111 - 112 12 func Test_getStorageMultipleName(t *testing.T) { 113 13 iniStr := ` 114 14 [lfs] ··· 118 18 MINIO_BUCKET = gitea-attachment 119 19 120 20 [storage] 21 + STORAGE_TYPE = minio 121 22 MINIO_BUCKET = gitea-storage 122 23 ` 123 24 cfg, err := NewConfigProviderFromData(iniStr) 124 25 assert.NoError(t, err) 125 26 126 - { 127 - sec := cfg.Section("attachment") 128 - storageType := sec.Key("STORAGE_TYPE").MustString("") 129 - storage := getStorage(cfg, "attachments", storageType, sec) 27 + assert.NoError(t, loadAttachmentFrom(cfg)) 28 + assert.EqualValues(t, "gitea-attachment", Attachment.Storage.MinioConfig.Bucket) 130 29 131 - assert.EqualValues(t, "gitea-attachment", storage.Section.Key("MINIO_BUCKET").String()) 132 - } 133 - { 134 - sec := cfg.Section("lfs") 135 - storageType := sec.Key("STORAGE_TYPE").MustString("") 136 - storage := getStorage(cfg, "lfs", storageType, sec) 137 - 138 - assert.EqualValues(t, "gitea-lfs", storage.Section.Key("MINIO_BUCKET").String()) 139 - } 140 - { 141 - sec := cfg.Section("avatar") 142 - storageType := sec.Key("STORAGE_TYPE").MustString("") 143 - storage := getStorage(cfg, "avatars", storageType, sec) 30 + assert.NoError(t, loadLFSFrom(cfg)) 31 + assert.EqualValues(t, "gitea-lfs", LFS.Storage.MinioConfig.Bucket) 144 32 145 - assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) 146 - } 33 + assert.NoError(t, loadAvatarsFrom(cfg)) 34 + assert.EqualValues(t, "gitea-storage", Avatar.Storage.MinioConfig.Bucket) 147 35 } 148 36 149 37 func Test_getStorageUseOtherNameAsType(t *testing.T) { ··· 152 40 STORAGE_TYPE = lfs 153 41 154 42 [storage.lfs] 43 + STORAGE_TYPE = minio 155 44 MINIO_BUCKET = gitea-storage 156 45 ` 157 46 cfg, err := NewConfigProviderFromData(iniStr) 158 47 assert.NoError(t, err) 159 48 160 - { 161 - sec := cfg.Section("attachment") 162 - storageType := sec.Key("STORAGE_TYPE").MustString("") 163 - storage := getStorage(cfg, "attachments", storageType, sec) 164 - 165 - assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) 166 - } 167 - { 168 - sec := cfg.Section("lfs") 169 - storageType := sec.Key("STORAGE_TYPE").MustString("") 170 - storage := getStorage(cfg, "lfs", storageType, sec) 49 + assert.NoError(t, loadAttachmentFrom(cfg)) 50 + assert.EqualValues(t, "gitea-storage", Attachment.Storage.MinioConfig.Bucket) 171 51 172 - assert.EqualValues(t, "gitea-storage", storage.Section.Key("MINIO_BUCKET").String()) 173 - } 52 + assert.NoError(t, loadLFSFrom(cfg)) 53 + assert.EqualValues(t, "gitea-storage", LFS.Storage.MinioConfig.Bucket) 174 54 } 175 55 176 56 func Test_getStorageInheritStorageType(t *testing.T) { ··· 181 61 cfg, err := NewConfigProviderFromData(iniStr) 182 62 assert.NoError(t, err) 183 63 184 - sec := cfg.Section("attachment") 185 - storageType := sec.Key("STORAGE_TYPE").MustString("") 186 - storage := getStorage(cfg, "attachments", storageType, sec) 64 + assert.NoError(t, loadPackagesFrom(cfg)) 65 + assert.EqualValues(t, "minio", Packages.Storage.Type) 66 + assert.EqualValues(t, "gitea", Packages.Storage.MinioConfig.Bucket) 67 + assert.EqualValues(t, "packages/", Packages.Storage.MinioConfig.BasePath) 187 68 188 - assert.EqualValues(t, "minio", storage.Type) 189 - } 69 + assert.NoError(t, loadRepoArchiveFrom(cfg)) 70 + assert.EqualValues(t, "minio", RepoArchive.Storage.Type) 71 + assert.EqualValues(t, "gitea", RepoArchive.Storage.MinioConfig.Bucket) 72 + assert.EqualValues(t, "repo-archive/", RepoArchive.Storage.MinioConfig.BasePath) 190 73 191 - func Test_getStorageInheritNameSectionType(t *testing.T) { 192 - iniStr := ` 193 - [storage.attachments] 194 - STORAGE_TYPE = minio 195 - ` 196 - cfg, err := NewConfigProviderFromData(iniStr) 197 - assert.NoError(t, err) 74 + assert.NoError(t, loadActionsFrom(cfg)) 75 + assert.EqualValues(t, "minio", Actions.LogStorage.Type) 76 + assert.EqualValues(t, "gitea", Actions.LogStorage.MinioConfig.Bucket) 77 + assert.EqualValues(t, "actions_log/", Actions.LogStorage.MinioConfig.BasePath) 198 78 199 - sec := cfg.Section("attachment") 200 - storageType := sec.Key("STORAGE_TYPE").MustString("") 201 - storage := getStorage(cfg, "attachments", storageType, sec) 79 + assert.EqualValues(t, "minio", Actions.ArtifactStorage.Type) 80 + assert.EqualValues(t, "gitea", Actions.ArtifactStorage.MinioConfig.Bucket) 81 + assert.EqualValues(t, "actions_artifacts/", Actions.ArtifactStorage.MinioConfig.BasePath) 82 + 83 + assert.NoError(t, loadAvatarsFrom(cfg)) 84 + assert.EqualValues(t, "minio", Avatar.Storage.Type) 85 + assert.EqualValues(t, "gitea", Avatar.Storage.MinioConfig.Bucket) 86 + assert.EqualValues(t, "avatars/", Avatar.Storage.MinioConfig.BasePath) 202 87 203 - assert.EqualValues(t, "minio", storage.Type) 88 + assert.NoError(t, loadRepoAvatarFrom(cfg)) 89 + assert.EqualValues(t, "minio", RepoAvatar.Storage.Type) 90 + assert.EqualValues(t, "gitea", RepoAvatar.Storage.MinioConfig.Bucket) 91 + assert.EqualValues(t, "repo-avatars/", RepoAvatar.Storage.MinioConfig.BasePath) 204 92 }
-56
modules/storage/helper.go
··· 8 8 "io" 9 9 "net/url" 10 10 "os" 11 - "reflect" 12 - 13 - "code.gitea.io/gitea/modules/json" 14 11 ) 15 - 16 - // Mappable represents an interface that can MapTo another interface 17 - type Mappable interface { 18 - MapTo(v interface{}) error 19 - } 20 - 21 - // toConfig will attempt to convert a given configuration cfg into the provided exemplar type. 22 - // 23 - // It will tolerate the cfg being passed as a []byte or string of a json representation of the 24 - // exemplar or the correct type of the exemplar itself 25 - func toConfig(exemplar, cfg interface{}) (interface{}, error) { 26 - // First of all check if we've got the same type as the exemplar - if so it's all fine. 27 - if reflect.TypeOf(cfg).AssignableTo(reflect.TypeOf(exemplar)) { 28 - return cfg, nil 29 - } 30 - 31 - // Now if not - does it provide a MapTo function we can try? 32 - if mappable, ok := cfg.(Mappable); ok { 33 - newVal := reflect.New(reflect.TypeOf(exemplar)) 34 - if err := mappable.MapTo(newVal.Interface()); err == nil { 35 - return newVal.Elem().Interface(), nil 36 - } 37 - // MapTo has failed us ... let's try the json route ... 38 - } 39 - 40 - // OK we've been passed a byte array right? 41 - configBytes, ok := cfg.([]byte) 42 - if !ok { 43 - // oh ... it's a string then? 44 - var configStr string 45 - 46 - configStr, ok = cfg.(string) 47 - configBytes = []byte(configStr) 48 - } 49 - if !ok { 50 - // hmm ... can we marshal it to json? 51 - var err error 52 - configBytes, err = json.Marshal(cfg) 53 - ok = err == nil 54 - } 55 - if !ok { 56 - // no ... we've tried hard enough at this point - throw an error! 57 - return nil, ErrInvalidConfiguration{cfg: cfg} 58 - } 59 - 60 - // OK unmarshal the byte array into a new copy of the exemplar 61 - newVal := reflect.New(reflect.TypeOf(exemplar)) 62 - if err := json.Unmarshal(configBytes, newVal.Interface()); err != nil { 63 - // If we can't unmarshal it then return an error! 64 - return nil, ErrInvalidConfiguration{cfg: cfg, err: err} 65 - } 66 - return newVal.Elem().Interface(), nil 67 - } 68 12 69 13 var uninitializedStorage = discardStorage("uninitialized storage") 70 14
+3 -17
modules/storage/local.go
··· 12 12 "path/filepath" 13 13 14 14 "code.gitea.io/gitea/modules/log" 15 + "code.gitea.io/gitea/modules/setting" 15 16 "code.gitea.io/gitea/modules/util" 16 17 ) 17 18 18 19 var _ ObjectStorage = &LocalStorage{} 19 20 20 - // LocalStorageType is the type descriptor for local storage 21 - const LocalStorageType Type = "local" 22 - 23 - // LocalStorageConfig represents the configuration for a local storage 24 - type LocalStorageConfig struct { 25 - Path string `ini:"PATH"` 26 - TemporaryPath string `ini:"TEMPORARY_PATH"` 27 - } 28 - 29 21 // LocalStorage represents a local files storage 30 22 type LocalStorage struct { 31 23 ctx context.Context ··· 34 26 } 35 27 36 28 // NewLocalStorage returns a local files 37 - func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error) { 38 - configInterface, err := toConfig(LocalStorageConfig{}, cfg) 39 - if err != nil { 40 - return nil, err 41 - } 42 - config := configInterface.(LocalStorageConfig) 43 - 29 + func NewLocalStorage(ctx context.Context, config *setting.Storage) (ObjectStorage, error) { 44 30 if !filepath.IsAbs(config.Path) { 45 31 return nil, fmt.Errorf("LocalStorageConfig.Path should have been prepared by setting/storage.go and should be an absolute path, but not: %q", config.Path) 46 32 } ··· 164 150 } 165 151 166 152 func init() { 167 - RegisterStorageType(LocalStorageType, NewLocalStorage) 153 + RegisterStorageType(setting.LocalStorageType, NewLocalStorage) 168 154 }
+3 -1
modules/storage/local_test.go
··· 8 8 "path/filepath" 9 9 "testing" 10 10 11 + "code.gitea.io/gitea/modules/setting" 12 + 11 13 "github.com/stretchr/testify/assert" 12 14 ) 13 15 ··· 55 57 56 58 func TestLocalStorageIterator(t *testing.T) { 57 59 dir := filepath.Join(os.TempDir(), "TestLocalStorageIteratorTestDir") 58 - testStorageIterator(t, string(LocalStorageType), LocalStorageConfig{Path: dir}) 60 + testStorageIterator(t, setting.LocalStorageType, &setting.Storage{Path: dir}) 59 61 }
+5 -25
modules/storage/minio.go
··· 16 16 "time" 17 17 18 18 "code.gitea.io/gitea/modules/log" 19 + "code.gitea.io/gitea/modules/setting" 19 20 "code.gitea.io/gitea/modules/util" 20 21 21 22 "github.com/minio/minio-go/v7" ··· 41 42 return &minioFileInfo{oi}, nil 42 43 } 43 44 44 - // MinioStorageType is the type descriptor for minio storage 45 - const MinioStorageType Type = "minio" 46 - 47 - // MinioStorageConfig represents the configuration for a minio storage 48 - type MinioStorageConfig struct { 49 - Endpoint string `ini:"MINIO_ENDPOINT"` 50 - AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID"` 51 - SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY"` 52 - Bucket string `ini:"MINIO_BUCKET"` 53 - Location string `ini:"MINIO_LOCATION"` 54 - BasePath string `ini:"MINIO_BASE_PATH"` 55 - UseSSL bool `ini:"MINIO_USE_SSL"` 56 - InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"` 57 - ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM"` 58 - } 59 - 60 45 // MinioStorage returns a minio bucket storage 61 46 type MinioStorage struct { 62 - cfg *MinioStorageConfig 47 + cfg *setting.MinioStorageConfig 63 48 ctx context.Context 64 49 client *minio.Client 65 50 bucket string ··· 87 72 } 88 73 89 74 // NewMinioStorage returns a minio storage 90 - func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error) { 91 - configInterface, err := toConfig(MinioStorageConfig{}, cfg) 92 - if err != nil { 93 - return nil, convertMinioErr(err) 94 - } 95 - config := configInterface.(MinioStorageConfig) 96 - 75 + func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) { 76 + config := cfg.MinioConfig 97 77 if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" { 98 78 return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm) 99 79 } ··· 258 238 } 259 239 260 240 func init() { 261 - RegisterStorageType(MinioStorageType, NewMinioStorage) 241 + RegisterStorageType(setting.MinioStorageType, NewMinioStorage) 262 242 }
+10 -6
modules/storage/minio_test.go
··· 6 6 import ( 7 7 "os" 8 8 "testing" 9 + 10 + "code.gitea.io/gitea/modules/setting" 9 11 ) 10 12 11 13 func TestMinioStorageIterator(t *testing.T) { ··· 13 15 t.Skip("minioStorage not present outside of CI") 14 16 return 15 17 } 16 - testStorageIterator(t, string(MinioStorageType), MinioStorageConfig{ 17 - Endpoint: "127.0.0.1:9000", 18 - AccessKeyID: "123456", 19 - SecretAccessKey: "12345678", 20 - Bucket: "gitea", 21 - Location: "us-east-1", 18 + testStorageIterator(t, setting.MinioStorageType, &setting.Storage{ 19 + MinioConfig: setting.MinioStorageConfig{ 20 + Endpoint: "127.0.0.1:9000", 21 + AccessKeyID: "123456", 22 + SecretAccessKey: "12345678", 23 + Bucket: "gitea", 24 + Location: "us-east-1", 25 + }, 22 26 }) 23 27 }
+14 -15
modules/storage/storage.go
··· 37 37 return ok 38 38 } 39 39 40 - // Type is a type of Storage 41 - type Type string 40 + type Type = setting.StorageType 42 41 43 42 // NewStorageFunc is a function that creates a storage 44 - type NewStorageFunc func(ctx context.Context, cfg interface{}) (ObjectStorage, error) 43 + type NewStorageFunc func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) 45 44 46 45 var storageMap = map[Type]NewStorageFunc{} 47 46 48 47 // 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)) { 48 + func RegisterStorageType(typ Type, fn func(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error)) { 50 49 storageMap[typ] = fn 51 50 } 52 51 ··· 151 150 } 152 151 153 152 // NewStorage takes a storage type and some config and returns an ObjectStorage or an error 154 - func NewStorage(typStr string, cfg interface{}) (ObjectStorage, error) { 153 + func NewStorage(typStr Type, cfg *setting.Storage) (ObjectStorage, error) { 155 154 if len(typStr) == 0 { 156 - typStr = string(LocalStorageType) 155 + typStr = setting.LocalStorageType 157 156 } 158 - fn, ok := storageMap[Type(typStr)] 157 + fn, ok := storageMap[typStr] 159 158 if !ok { 160 159 return nil, fmt.Errorf("Unsupported storage type: %s", typStr) 161 160 } ··· 165 164 166 165 func initAvatars() (err error) { 167 166 log.Info("Initialising Avatar storage with type: %s", setting.Avatar.Storage.Type) 168 - Avatars, err = NewStorage(setting.Avatar.Storage.Type, &setting.Avatar.Storage) 167 + Avatars, err = NewStorage(setting.Avatar.Storage.Type, setting.Avatar.Storage) 169 168 return err 170 169 } 171 170 ··· 175 174 return nil 176 175 } 177 176 log.Info("Initialising Attachment storage with type: %s", setting.Attachment.Storage.Type) 178 - Attachments, err = NewStorage(setting.Attachment.Storage.Type, &setting.Attachment.Storage) 177 + Attachments, err = NewStorage(setting.Attachment.Storage.Type, setting.Attachment.Storage) 179 178 return err 180 179 } 181 180 ··· 185 184 return nil 186 185 } 187 186 log.Info("Initialising LFS storage with type: %s", setting.LFS.Storage.Type) 188 - LFS, err = NewStorage(setting.LFS.Storage.Type, &setting.LFS.Storage) 187 + LFS, err = NewStorage(setting.LFS.Storage.Type, setting.LFS.Storage) 189 188 return err 190 189 } 191 190 192 191 func initRepoAvatars() (err error) { 193 192 log.Info("Initialising Repository Avatar storage with type: %s", setting.RepoAvatar.Storage.Type) 194 - RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, &setting.RepoAvatar.Storage) 193 + RepoAvatars, err = NewStorage(setting.RepoAvatar.Storage.Type, setting.RepoAvatar.Storage) 195 194 return err 196 195 } 197 196 198 197 func initRepoArchives() (err error) { 199 198 log.Info("Initialising Repository Archive storage with type: %s", setting.RepoArchive.Storage.Type) 200 - RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, &setting.RepoArchive.Storage) 199 + RepoArchives, err = NewStorage(setting.RepoArchive.Storage.Type, setting.RepoArchive.Storage) 201 200 return err 202 201 } 203 202 ··· 207 206 return nil 208 207 } 209 208 log.Info("Initialising Packages storage with type: %s", setting.Packages.Storage.Type) 210 - Packages, err = NewStorage(setting.Packages.Storage.Type, &setting.Packages.Storage) 209 + Packages, err = NewStorage(setting.Packages.Storage.Type, setting.Packages.Storage) 211 210 return err 212 211 } 213 212 ··· 218 217 return nil 219 218 } 220 219 log.Info("Initialising Actions storage with type: %s", setting.Actions.LogStorage.Type) 221 - if Actions, err = NewStorage(setting.Actions.LogStorage.Type, &setting.Actions.LogStorage); err != nil { 220 + if Actions, err = NewStorage(setting.Actions.LogStorage.Type, setting.Actions.LogStorage); err != nil { 222 221 return err 223 222 } 224 223 log.Info("Initialising ActionsArtifacts storage with type: %s", setting.Actions.ArtifactStorage.Type) 225 - ActionsArtifacts, err = NewStorage(setting.Actions.ArtifactStorage.Type, &setting.Actions.ArtifactStorage) 224 + ActionsArtifacts, err = NewStorage(setting.Actions.ArtifactStorage.Type, setting.Actions.ArtifactStorage) 226 225 return err 227 226 }
+3 -1
modules/storage/storage_test.go
··· 7 7 "bytes" 8 8 "testing" 9 9 10 + "code.gitea.io/gitea/modules/setting" 11 + 10 12 "github.com/stretchr/testify/assert" 11 13 ) 12 14 13 - func testStorageIterator(t *testing.T, typStr string, cfg interface{}) { 15 + func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) { 14 16 l, err := NewStorage(typStr, cfg) 15 17 assert.NoError(t, err) 16 18
+2 -2
routers/api/v1/repo/file.go
··· 200 200 return 201 201 } 202 202 203 - if setting.LFS.ServeDirect { 203 + if setting.LFS.Storage.MinioConfig.ServeDirect { 204 204 // If we have a signed url (S3, object storage), redirect to this directly. 205 205 u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name()) 206 206 if u != nil && err == nil { ··· 320 320 downloadName := ctx.Repo.Repository.Name + "-" + archiveName 321 321 322 322 rPath := archiver.RelativePath() 323 - if setting.RepoArchive.ServeDirect { 323 + if setting.RepoArchive.Storage.MinioConfig.ServeDirect { 324 324 // If we have a signed url (S3, object storage), redirect to this directly. 325 325 u, err := storage.RepoArchives.URL(rPath, downloadName) 326 326 if u != nil && err == nil {
+1 -1
routers/install/install.go
··· 116 116 // Application general settings 117 117 form.AppName = setting.AppName 118 118 form.RepoRootPath = setting.RepoRootPath 119 - form.LFSRootPath = setting.LFS.Path 119 + form.LFSRootPath = setting.LFS.Storage.Path 120 120 121 121 // Note(unknown): it's hard for Windows users change a running user, 122 122 // so just use current one if config says default.
+2 -2
routers/web/base.go
··· 19 19 "code.gitea.io/gitea/modules/web/routing" 20 20 ) 21 21 22 - func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { 22 + func storageHandler(storageSetting *setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler { 23 23 prefix = strings.Trim(prefix, "/") 24 24 funcInfo := routing.GetFuncInfo(storageHandler, prefix) 25 25 return func(next http.Handler) http.Handler { 26 - if storageSetting.ServeDirect { 26 + if storageSetting.MinioConfig.ServeDirect { 27 27 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 28 28 if req.Method != "GET" && req.Method != "HEAD" { 29 29 next.ServeHTTP(w, req)
+1 -1
routers/web/repo/attachment.go
··· 126 126 return 127 127 } 128 128 129 - if setting.Attachment.ServeDirect { 129 + if setting.Attachment.Storage.MinioConfig.ServeDirect { 130 130 // If we have a signed url (S3, object storage), redirect to this directly. 131 131 u, err := storage.Attachments.URL(attach.RelativePath(), attach.Name) 132 132
+1 -1
routers/web/repo/download.go
··· 53 53 return nil 54 54 } 55 55 56 - if setting.LFS.ServeDirect { 56 + if setting.LFS.Storage.MinioConfig.ServeDirect { 57 57 // If we have a signed url (S3, object storage), redirect to this directly. 58 58 u, err := storage.LFS.URL(pointer.RelativePath(), blob.Name()) 59 59 if u != nil && err == nil {
+1 -1
routers/web/repo/repo.go
··· 427 427 downloadName := ctx.Repo.Repository.Name + "-" + archiveName 428 428 429 429 rPath := archiver.RelativePath() 430 - if setting.RepoArchive.ServeDirect { 430 + if setting.RepoArchive.Storage.MinioConfig.ServeDirect { 431 431 // If we have a signed url (S3, object storage), redirect to this directly. 432 432 u, err := storage.RepoArchives.URL(rPath, downloadName) 433 433 if u != nil && err == nil {
+1 -1
services/lfs/server.go
··· 452 452 453 453 if download { 454 454 var link *lfs_module.Link 455 - if setting.LFS.ServeDirect { 455 + if setting.LFS.Storage.MinioConfig.ServeDirect { 456 456 // If we have a signed url (S3, object storage), redirect to this directly. 457 457 u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid) 458 458 if u != nil && err == nil {
+1 -1
templates/admin/config.tmpl
··· 102 102 <dd>{{if .LFS.StartServer}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</dd> 103 103 {{if .LFS.StartServer}} 104 104 <dt>{{.locale.Tr "admin.config.lfs_content_path"}}</dt> 105 - <dd>{{.LFS.Path}}</dd> 105 + <dd>{{JsonUtils.EncodeToString .LFS.Storage.ToShadowCopy}}</dd> 106 106 <dt>{{.locale.Tr "admin.config.lfs_http_auth_expiry"}}</dt> 107 107 <dd>{{.LFS.HTTPAuthExpiry}}</dd> 108 108 {{end}}
+3 -1
tests/test_utils.go
··· 214 214 215 215 // load LFS object fixtures 216 216 // (LFS storage can be on any of several backends, including remote servers, so we init it with the storage API) 217 - lfsFixtures, err := storage.NewStorage("", storage.LocalStorageConfig{Path: path.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta")}) 217 + lfsFixtures, err := storage.NewStorage(setting.LocalStorageType, &setting.Storage{ 218 + Path: filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-lfs-meta"), 219 + }) 218 220 assert.NoError(t, err) 219 221 assert.NoError(t, storage.Clean(storage.LFS)) 220 222 assert.NoError(t, lfsFixtures.IterateObjects("", func(path string, _ storage.Object) error {