code.gitea.io/gitea@v1.22.3/modules/setting/storage.go (about)

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