| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- // Copyright 2020 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package setting
-
- import (
- "errors"
- "fmt"
- "path/filepath"
- "slices"
- "strings"
- )
-
- // StorageType is a type of Storage
- type StorageType string
-
- const (
- // LocalStorageType is the type descriptor for local storage
- LocalStorageType StorageType = "local"
- // MinioStorageType is the type descriptor for minio storage
- MinioStorageType StorageType = "minio"
- // AzureBlobStorageType is the type descriptor for azure blob storage
- AzureBlobStorageType StorageType = "azureblob"
- )
-
- var storageTypes = []StorageType{
- LocalStorageType,
- MinioStorageType,
- AzureBlobStorageType,
- }
-
- // IsValidStorageType returns true if the given storage type is valid
- func IsValidStorageType(storageType StorageType) bool {
- return slices.Contains(storageTypes, storageType)
- }
-
- // MinioStorageConfig represents the configuration for a minio storage
- type MinioStorageConfig struct {
- Endpoint string `ini:"MINIO_ENDPOINT" json:",omitempty"`
- AccessKeyID string `ini:"MINIO_ACCESS_KEY_ID" json:",omitempty"`
- SecretAccessKey string `ini:"MINIO_SECRET_ACCESS_KEY" json:",omitempty"`
- IamEndpoint string `ini:"MINIO_IAM_ENDPOINT" json:",omitempty"`
- Bucket string `ini:"MINIO_BUCKET" json:",omitempty"`
- Location string `ini:"MINIO_LOCATION" json:",omitempty"`
- BasePath string `ini:"MINIO_BASE_PATH" json:",omitempty"`
- UseSSL bool `ini:"MINIO_USE_SSL"`
- InsecureSkipVerify bool `ini:"MINIO_INSECURE_SKIP_VERIFY"`
- ChecksumAlgorithm string `ini:"MINIO_CHECKSUM_ALGORITHM" json:",omitempty"`
- ServeDirect bool `ini:"SERVE_DIRECT"`
- BucketLookUpType string `ini:"MINIO_BUCKET_LOOKUP_TYPE" json:",omitempty"`
- }
-
- func (cfg *MinioStorageConfig) ToShadow() {
- if cfg.AccessKeyID != "" {
- cfg.AccessKeyID = "******"
- }
- if cfg.SecretAccessKey != "" {
- cfg.SecretAccessKey = "******"
- }
- }
-
- // MinioStorageConfig represents the configuration for a minio storage
- type AzureBlobStorageConfig struct {
- Endpoint string `ini:"AZURE_BLOB_ENDPOINT" json:",omitempty"`
- AccountName string `ini:"AZURE_BLOB_ACCOUNT_NAME" json:",omitempty"`
- AccountKey string `ini:"AZURE_BLOB_ACCOUNT_KEY" json:",omitempty"`
- Container string `ini:"AZURE_BLOB_CONTAINER" json:",omitempty"`
- BasePath string `ini:"AZURE_BLOB_BASE_PATH" json:",omitempty"`
- ServeDirect bool `ini:"SERVE_DIRECT"`
- }
-
- func (cfg *AzureBlobStorageConfig) ToShadow() {
- if cfg.AccountKey != "" {
- cfg.AccountKey = "******"
- }
- if cfg.AccountName != "" {
- cfg.AccountName = "******"
- }
- }
-
- // Storage represents configuration of storages
- type Storage struct {
- Type StorageType // local or minio or azureblob
- Path string `json:",omitempty"` // for local type
- TemporaryPath string `json:",omitempty"`
- MinioConfig MinioStorageConfig // for minio type
- AzureBlobConfig AzureBlobStorageConfig // for azureblob type
- }
-
- func (storage *Storage) ToShadowCopy() Storage {
- shadowStorage := *storage
- shadowStorage.MinioConfig.ToShadow()
- shadowStorage.AzureBlobConfig.ToShadow()
- return shadowStorage
- }
-
- func (storage *Storage) ServeDirect() bool {
- return (storage.Type == MinioStorageType && storage.MinioConfig.ServeDirect) ||
- (storage.Type == AzureBlobStorageType && storage.AzureBlobConfig.ServeDirect)
- }
-
- const storageSectionName = "storage"
-
- func getDefaultStorageSection(rootCfg ConfigProvider) ConfigSection {
- storageSec := rootCfg.Section(storageSectionName)
- // Global Defaults
- storageSec.Key("STORAGE_TYPE").MustString("local")
- storageSec.Key("MINIO_ENDPOINT").MustString("localhost:9000")
- storageSec.Key("MINIO_ACCESS_KEY_ID").MustString("")
- storageSec.Key("MINIO_SECRET_ACCESS_KEY").MustString("")
- storageSec.Key("MINIO_BUCKET").MustString("gitea")
- storageSec.Key("MINIO_LOCATION").MustString("us-east-1")
- storageSec.Key("MINIO_USE_SSL").MustBool(false)
- storageSec.Key("MINIO_INSECURE_SKIP_VERIFY").MustBool(false)
- storageSec.Key("MINIO_CHECKSUM_ALGORITHM").MustString("default")
- storageSec.Key("MINIO_BUCKET_LOOKUP_TYPE").MustString("auto")
- storageSec.Key("AZURE_BLOB_ENDPOINT").MustString("")
- storageSec.Key("AZURE_BLOB_ACCOUNT_NAME").MustString("")
- storageSec.Key("AZURE_BLOB_ACCOUNT_KEY").MustString("")
- storageSec.Key("AZURE_BLOB_CONTAINER").MustString("gitea")
- return storageSec
- }
-
- // getStorage will find target section and extra special section first and then read override
- // items from extra section
- func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*Storage, error) {
- if name == "" {
- return nil, errors.New("no name for storage")
- }
-
- targetSec, tp, err := getStorageTargetSection(rootCfg, name, typ, sec)
- if err != nil {
- return nil, err
- }
-
- overrideSec := getStorageOverrideSection(rootCfg, sec, tp, name)
-
- targetType := targetSec.Key("STORAGE_TYPE").String()
- switch targetType {
- case string(LocalStorageType):
- return getStorageForLocal(targetSec, overrideSec, tp, name)
- case string(MinioStorageType):
- return getStorageForMinio(targetSec, overrideSec, tp, name)
- case string(AzureBlobStorageType):
- return getStorageForAzureBlob(targetSec, overrideSec, tp, name)
- default:
- return nil, fmt.Errorf("unsupported storage type %q", targetType)
- }
- }
-
- type targetSecType int
-
- const (
- targetSecIsTyp targetSecType = iota // target section is [storage.type] which the type from parameter
- targetSecIsStorage // target section is [storage]
- targetSecIsDefault // target section is the default value
- targetSecIsStorageWithName // target section is [storage.name]
- targetSecIsSec // target section is from the name seciont [name]
- )
-
- func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { //nolint:unparam // FIXME: targetSecType is always 0, wrong design?
- targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ)
- if err != nil {
- if !IsValidStorageType(StorageType(typ)) {
- return nil, 0, fmt.Errorf("get section via storage type %q failed: %v", typ, err)
- }
- // if typ is a valid storage type, but there is no [storage.local] or [storage.minio] section
- // it's not an error
- return nil, 0, nil
- }
-
- targetType := targetSec.Key("STORAGE_TYPE").String()
- if targetType == "" {
- if !IsValidStorageType(StorageType(typ)) {
- return nil, 0, fmt.Errorf("unknow storage type %q", typ)
- }
- targetSec.Key("STORAGE_TYPE").SetValue(typ)
- } else if !IsValidStorageType(StorageType(targetType)) {
- return nil, 0, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ)
- }
-
- return targetSec, targetSecIsTyp, nil
- }
-
- func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (ConfigSection, targetSecType, error) {
- // check typ first
- if typ == "" {
- if sec != nil { // check sec's type secondly
- typ = sec.Key("STORAGE_TYPE").String()
- if IsValidStorageType(StorageType(typ)) {
- if targetSec, _ := rootCfg.GetSection(storageSectionName + "." + typ); targetSec == nil {
- return sec, targetSecIsSec, nil
- }
- }
- }
- }
-
- if typ != "" {
- targetSec, tp, err := getStorageSectionByType(rootCfg, typ)
- if targetSec != nil || err != nil {
- return targetSec, tp, err
- }
- }
-
- // check stoarge name thirdly
- targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name)
- if targetSec != nil {
- targetType := targetSec.Key("STORAGE_TYPE").String()
- switch targetType {
- case "":
- if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default
- return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil
- }
-
- targetSec.Key("STORAGE_TYPE").SetValue("local")
- default:
- targetSec, tp, err := getStorageSectionByType(rootCfg, targetType)
- if targetSec != nil || err != nil {
- return targetSec, tp, err
- }
- }
-
- return targetSec, targetSecIsStorageWithName, nil
- }
-
- return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil
- }
-
- // getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible
- func getStorageOverrideSection(rootConfig ConfigProvider, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection {
- if targetSecType == targetSecIsSec {
- return nil
- }
-
- if sec != nil {
- return sec
- }
-
- if targetSecType != targetSecIsStorageWithName {
- nameSec, _ := rootConfig.GetSection(storageSectionName + "." + name)
- return nameSec
- }
- return nil
- }
-
- func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) {
- storage := Storage{
- Type: StorageType(targetSec.Key("STORAGE_TYPE").String()),
- }
-
- targetPath := ConfigSectionKeyString(targetSec, "PATH", "")
- var fallbackPath string
- if targetPath == "" { // no path
- fallbackPath = filepath.Join(AppDataPath, name)
- } else {
- if tp == targetSecIsStorage || tp == targetSecIsDefault {
- fallbackPath = filepath.Join(targetPath, name)
- } else {
- fallbackPath = targetPath
- }
- if !filepath.IsAbs(fallbackPath) {
- fallbackPath = filepath.Join(AppDataPath, fallbackPath)
- }
- }
-
- if overrideSec == nil { // no override section
- storage.Path = fallbackPath
- } else {
- storage.Path = ConfigSectionKeyString(overrideSec, "PATH", "")
- if storage.Path == "" { // overrideSec has no path
- storage.Path = fallbackPath
- } else if !filepath.IsAbs(storage.Path) {
- if targetPath == "" {
- storage.Path = filepath.Join(AppDataPath, storage.Path)
- } else {
- storage.Path = filepath.Join(targetPath, storage.Path)
- }
- }
- }
-
- checkOverlappedPath("[storage."+name+"].PATH", storage.Path)
-
- return &storage, nil
- }
-
- func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates azure setup
- var storage Storage
- storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String())
- if err := targetSec.MapTo(&storage.MinioConfig); err != nil {
- return nil, fmt.Errorf("map minio config failed: %v", err)
- }
-
- var defaultPath string
- if storage.MinioConfig.BasePath != "" {
- if tp == targetSecIsStorage || tp == targetSecIsDefault {
- defaultPath = strings.TrimSuffix(storage.MinioConfig.BasePath, "/") + "/" + name + "/"
- } else {
- defaultPath = storage.MinioConfig.BasePath
- }
- }
- if defaultPath == "" {
- defaultPath = name + "/"
- }
-
- if overrideSec != nil {
- storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect)
- storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", defaultPath)
- storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket)
- } else {
- storage.MinioConfig.BasePath = defaultPath
- }
- return &storage, nil
- }
-
- func getStorageForAzureBlob(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates minio setup
- var storage Storage
- storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String())
- if err := targetSec.MapTo(&storage.AzureBlobConfig); err != nil {
- return nil, fmt.Errorf("map azure blob config failed: %v", err)
- }
-
- var defaultPath string
- if storage.AzureBlobConfig.BasePath != "" {
- if tp == targetSecIsStorage || tp == targetSecIsDefault {
- defaultPath = strings.TrimSuffix(storage.AzureBlobConfig.BasePath, "/") + "/" + name + "/"
- } else {
- defaultPath = storage.AzureBlobConfig.BasePath
- }
- }
- if defaultPath == "" {
- defaultPath = name + "/"
- }
-
- if overrideSec != nil {
- storage.AzureBlobConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.AzureBlobConfig.ServeDirect)
- storage.AzureBlobConfig.BasePath = ConfigSectionKeyString(overrideSec, "AZURE_BLOB_BASE_PATH", defaultPath)
- storage.AzureBlobConfig.Container = ConfigSectionKeyString(overrideSec, "AZURE_BLOB_CONTAINER", storage.AzureBlobConfig.Container)
- } else {
- storage.AzureBlobConfig.BasePath = defaultPath
- }
- return &storage, nil
- }
|