mirror of
https://github.com/minio/minio.git
synced 2024-12-27 23:55:56 -05:00
989c318a28
This PR also improves throttling by reducing tokens requested from rate limiter based on available tokens to avoid exceeding throttle wait deadlines
356 lines
12 KiB
Go
356 lines
12 KiB
Go
// Copyright (c) 2015-2023 MinIO, Inc.
|
|
//
|
|
// This file is part of MinIO Object Storage stack
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/minio/minio/internal/config"
|
|
"github.com/minio/pkg/v3/env"
|
|
)
|
|
|
|
// API sub-system constants
|
|
const (
|
|
apiRequestsMax = "requests_max"
|
|
apiRequestsDeadline = "requests_deadline"
|
|
apiClusterDeadline = "cluster_deadline"
|
|
apiCorsAllowOrigin = "cors_allow_origin"
|
|
apiRemoteTransportDeadline = "remote_transport_deadline"
|
|
apiListQuorum = "list_quorum"
|
|
apiReplicationPriority = "replication_priority"
|
|
apiReplicationMaxWorkers = "replication_max_workers"
|
|
apiReplicationMaxLWorkers = "replication_max_lrg_workers"
|
|
|
|
apiTransitionWorkers = "transition_workers"
|
|
apiStaleUploadsCleanupInterval = "stale_uploads_cleanup_interval"
|
|
apiStaleUploadsExpiry = "stale_uploads_expiry"
|
|
apiDeleteCleanupInterval = "delete_cleanup_interval"
|
|
apiDisableODirect = "disable_odirect"
|
|
apiODirect = "odirect"
|
|
apiGzipObjects = "gzip_objects"
|
|
apiRootAccess = "root_access"
|
|
apiSyncEvents = "sync_events"
|
|
apiObjectMaxVersions = "object_max_versions"
|
|
|
|
EnvAPIRequestsMax = "MINIO_API_REQUESTS_MAX"
|
|
EnvAPIRequestsDeadline = "MINIO_API_REQUESTS_DEADLINE"
|
|
EnvAPIClusterDeadline = "MINIO_API_CLUSTER_DEADLINE"
|
|
EnvAPICorsAllowOrigin = "MINIO_API_CORS_ALLOW_ORIGIN"
|
|
EnvAPIRemoteTransportDeadline = "MINIO_API_REMOTE_TRANSPORT_DEADLINE"
|
|
EnvAPITransitionWorkers = "MINIO_API_TRANSITION_WORKERS"
|
|
EnvAPIListQuorum = "MINIO_API_LIST_QUORUM"
|
|
EnvAPISecureCiphers = "MINIO_API_SECURE_CIPHERS" // default config.EnableOn
|
|
EnvAPIReplicationPriority = "MINIO_API_REPLICATION_PRIORITY"
|
|
EnvAPIReplicationMaxWorkers = "MINIO_API_REPLICATION_MAX_WORKERS"
|
|
EnvAPIReplicationMaxLWorkers = "MINIO_API_REPLICATION_MAX_LRG_WORKERS"
|
|
|
|
EnvAPIStaleUploadsCleanupInterval = "MINIO_API_STALE_UPLOADS_CLEANUP_INTERVAL"
|
|
EnvAPIStaleUploadsExpiry = "MINIO_API_STALE_UPLOADS_EXPIRY"
|
|
EnvAPIDeleteCleanupInterval = "MINIO_API_DELETE_CLEANUP_INTERVAL"
|
|
EnvDeleteCleanupInterval = "MINIO_DELETE_CLEANUP_INTERVAL"
|
|
EnvAPIODirect = "MINIO_API_ODIRECT"
|
|
EnvAPIDisableODirect = "MINIO_API_DISABLE_ODIRECT"
|
|
EnvAPIGzipObjects = "MINIO_API_GZIP_OBJECTS"
|
|
EnvAPIRootAccess = "MINIO_API_ROOT_ACCESS" // default config.EnableOn
|
|
EnvAPISyncEvents = "MINIO_API_SYNC_EVENTS" // default "off"
|
|
EnvAPIObjectMaxVersions = "MINIO_API_OBJECT_MAX_VERSIONS"
|
|
EnvAPIObjectMaxVersionsLegacy = "_MINIO_OBJECT_MAX_VERSIONS"
|
|
)
|
|
|
|
// Deprecated key and ENVs
|
|
const (
|
|
apiReadyDeadline = "ready_deadline"
|
|
apiReplicationWorkers = "replication_workers"
|
|
apiReplicationFailedWorkers = "replication_failed_workers"
|
|
)
|
|
|
|
// DefaultKVS - default storage class config
|
|
var (
|
|
DefaultKVS = config.KVS{
|
|
config.KV{
|
|
Key: apiRequestsMax,
|
|
Value: "0",
|
|
},
|
|
config.KV{
|
|
Key: apiRequestsDeadline,
|
|
Value: "10s",
|
|
},
|
|
config.KV{
|
|
Key: apiClusterDeadline,
|
|
Value: "10s",
|
|
},
|
|
config.KV{
|
|
Key: apiCorsAllowOrigin,
|
|
Value: "*",
|
|
},
|
|
config.KV{
|
|
Key: apiRemoteTransportDeadline,
|
|
Value: "2h",
|
|
},
|
|
config.KV{
|
|
Key: apiListQuorum,
|
|
Value: "strict",
|
|
},
|
|
config.KV{
|
|
Key: apiReplicationPriority,
|
|
Value: "auto",
|
|
},
|
|
config.KV{
|
|
Key: apiReplicationMaxWorkers,
|
|
Value: "500",
|
|
},
|
|
config.KV{
|
|
Key: apiReplicationMaxLWorkers,
|
|
Value: "10",
|
|
},
|
|
config.KV{
|
|
Key: apiTransitionWorkers,
|
|
Value: "100",
|
|
},
|
|
config.KV{
|
|
Key: apiStaleUploadsCleanupInterval,
|
|
Value: "6h",
|
|
},
|
|
config.KV{
|
|
Key: apiStaleUploadsExpiry,
|
|
Value: "24h",
|
|
},
|
|
config.KV{
|
|
Key: apiDeleteCleanupInterval,
|
|
Value: "5m",
|
|
},
|
|
config.KV{
|
|
Key: apiDisableODirect,
|
|
Value: "",
|
|
HiddenIfEmpty: true,
|
|
},
|
|
config.KV{
|
|
Key: apiODirect,
|
|
Value: config.EnableOn,
|
|
},
|
|
config.KV{
|
|
Key: apiGzipObjects,
|
|
Value: config.EnableOff,
|
|
},
|
|
config.KV{
|
|
Key: apiRootAccess,
|
|
Value: config.EnableOn,
|
|
},
|
|
config.KV{
|
|
Key: apiSyncEvents,
|
|
Value: config.EnableOff,
|
|
},
|
|
config.KV{
|
|
Key: apiObjectMaxVersions,
|
|
Value: "9223372036854775807",
|
|
},
|
|
}
|
|
)
|
|
|
|
// Config storage class configuration
|
|
type Config struct {
|
|
RequestsMax int `json:"requests_max"`
|
|
RequestsDeadline time.Duration `json:"requests_deadline"`
|
|
ClusterDeadline time.Duration `json:"cluster_deadline"`
|
|
CorsAllowOrigin []string `json:"cors_allow_origin"`
|
|
RemoteTransportDeadline time.Duration `json:"remote_transport_deadline"`
|
|
ListQuorum string `json:"list_quorum"`
|
|
ReplicationPriority string `json:"replication_priority"`
|
|
ReplicationMaxWorkers int `json:"replication_max_workers"`
|
|
ReplicationMaxLWorkers int `json:"replication_max_lrg_workers"`
|
|
TransitionWorkers int `json:"transition_workers"`
|
|
StaleUploadsCleanupInterval time.Duration `json:"stale_uploads_cleanup_interval"`
|
|
StaleUploadsExpiry time.Duration `json:"stale_uploads_expiry"`
|
|
DeleteCleanupInterval time.Duration `json:"delete_cleanup_interval"`
|
|
EnableODirect bool `json:"enable_odirect"`
|
|
GzipObjects bool `json:"gzip_objects"`
|
|
RootAccess bool `json:"root_access"`
|
|
SyncEvents bool `json:"sync_events"`
|
|
ObjectMaxVersions int64 `json:"object_max_versions"`
|
|
}
|
|
|
|
// UnmarshalJSON - Validate SS and RRS parity when unmarshalling JSON.
|
|
func (sCfg *Config) UnmarshalJSON(data []byte) error {
|
|
type Alias Config
|
|
aux := &struct {
|
|
*Alias
|
|
}{
|
|
Alias: (*Alias)(sCfg),
|
|
}
|
|
return json.Unmarshal(data, &aux)
|
|
}
|
|
|
|
// LookupConfig - lookup api config and override with valid environment settings if any.
|
|
func LookupConfig(kvs config.KVS) (cfg Config, err error) {
|
|
deprecatedKeys := []string{
|
|
apiReadyDeadline,
|
|
"extend_list_cache_life",
|
|
apiReplicationWorkers,
|
|
apiReplicationFailedWorkers,
|
|
"expiry_workers",
|
|
}
|
|
|
|
disableODirect := env.Get(EnvAPIDisableODirect, kvs.Get(apiDisableODirect)) == config.EnableOn
|
|
enableODirect := env.Get(EnvAPIODirect, kvs.Get(apiODirect)) == config.EnableOn
|
|
gzipObjects := env.Get(EnvAPIGzipObjects, kvs.Get(apiGzipObjects)) == config.EnableOn
|
|
rootAccess := env.Get(EnvAPIRootAccess, kvs.Get(apiRootAccess)) == config.EnableOn
|
|
|
|
cfg = Config{
|
|
EnableODirect: enableODirect || !disableODirect,
|
|
GzipObjects: gzipObjects,
|
|
RootAccess: rootAccess,
|
|
}
|
|
|
|
var corsAllowOrigin []string
|
|
corsList := env.Get(EnvAPICorsAllowOrigin, kvs.Get(apiCorsAllowOrigin))
|
|
if corsList == "" {
|
|
corsAllowOrigin = []string{"*"} // defaults to '*'
|
|
} else {
|
|
corsAllowOrigin = strings.Split(corsList, ",")
|
|
for _, cors := range corsAllowOrigin {
|
|
if cors == "" {
|
|
return cfg, errors.New("invalid cors value")
|
|
}
|
|
}
|
|
}
|
|
cfg.CorsAllowOrigin = corsAllowOrigin
|
|
|
|
if err = config.CheckValidKeys(config.APISubSys, kvs, DefaultKVS, deprecatedKeys...); err != nil {
|
|
return cfg, err
|
|
}
|
|
|
|
// Check environment variables parameters
|
|
requestsMax, err := strconv.Atoi(env.Get(EnvAPIRequestsMax, kvs.GetWithDefault(apiRequestsMax, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
|
|
cfg.RequestsMax = requestsMax
|
|
if requestsMax < 0 {
|
|
return cfg, errors.New("invalid API max requests value")
|
|
}
|
|
|
|
requestsDeadline, err := time.ParseDuration(env.Get(EnvAPIRequestsDeadline, kvs.GetWithDefault(apiRequestsDeadline, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.RequestsDeadline = requestsDeadline
|
|
|
|
clusterDeadline, err := time.ParseDuration(env.Get(EnvAPIClusterDeadline, kvs.GetWithDefault(apiClusterDeadline, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.ClusterDeadline = clusterDeadline
|
|
|
|
remoteTransportDeadline, err := time.ParseDuration(env.Get(EnvAPIRemoteTransportDeadline, kvs.GetWithDefault(apiRemoteTransportDeadline, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.RemoteTransportDeadline = remoteTransportDeadline
|
|
|
|
listQuorum := env.Get(EnvAPIListQuorum, kvs.GetWithDefault(apiListQuorum, DefaultKVS))
|
|
switch listQuorum {
|
|
case "strict", "optimal", "reduced", "disk", "auto":
|
|
default:
|
|
return cfg, fmt.Errorf("invalid value %v for list_quorum: will default to 'strict'", listQuorum)
|
|
}
|
|
cfg.ListQuorum = listQuorum
|
|
|
|
replicationPriority := env.Get(EnvAPIReplicationPriority, kvs.GetWithDefault(apiReplicationPriority, DefaultKVS))
|
|
switch replicationPriority {
|
|
case "slow", "fast", "auto":
|
|
default:
|
|
return cfg, fmt.Errorf("invalid value %v for replication_priority", replicationPriority)
|
|
}
|
|
cfg.ReplicationPriority = replicationPriority
|
|
replicationMaxWorkers, err := strconv.Atoi(env.Get(EnvAPIReplicationMaxWorkers, kvs.GetWithDefault(apiReplicationMaxWorkers, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
if replicationMaxWorkers <= 0 || replicationMaxWorkers > 500 {
|
|
return cfg, config.ErrInvalidReplicationWorkersValue(nil).Msg("Number of replication workers should be between 1 and 500")
|
|
}
|
|
cfg.ReplicationMaxWorkers = replicationMaxWorkers
|
|
|
|
replicationMaxLWorkers, err := strconv.Atoi(env.Get(EnvAPIReplicationMaxLWorkers, kvs.GetWithDefault(apiReplicationMaxLWorkers, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
if replicationMaxLWorkers <= 0 || replicationMaxLWorkers > 10 {
|
|
return cfg, config.ErrInvalidReplicationWorkersValue(nil).Msg("Number of replication workers for transfers >=128MiB should be between 1 and 10 per node")
|
|
}
|
|
|
|
cfg.ReplicationMaxLWorkers = replicationMaxLWorkers
|
|
|
|
transitionWorkers, err := strconv.Atoi(env.Get(EnvAPITransitionWorkers, kvs.GetWithDefault(apiTransitionWorkers, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.TransitionWorkers = transitionWorkers
|
|
|
|
v := env.Get(EnvAPIDeleteCleanupInterval, kvs.Get(apiDeleteCleanupInterval))
|
|
if v == "" {
|
|
v = env.Get(EnvDeleteCleanupInterval, kvs.GetWithDefault(apiDeleteCleanupInterval, DefaultKVS))
|
|
}
|
|
|
|
deleteCleanupInterval, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.DeleteCleanupInterval = deleteCleanupInterval
|
|
|
|
staleUploadsCleanupInterval, err := time.ParseDuration(env.Get(EnvAPIStaleUploadsCleanupInterval, kvs.GetWithDefault(apiStaleUploadsCleanupInterval, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.StaleUploadsCleanupInterval = staleUploadsCleanupInterval
|
|
|
|
staleUploadsExpiry, err := time.ParseDuration(env.Get(EnvAPIStaleUploadsExpiry, kvs.GetWithDefault(apiStaleUploadsExpiry, DefaultKVS)))
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.StaleUploadsExpiry = staleUploadsExpiry
|
|
|
|
cfg.SyncEvents = env.Get(EnvAPISyncEvents, kvs.Get(apiSyncEvents)) == config.EnableOn
|
|
|
|
maxVerStr := env.Get(EnvAPIObjectMaxVersions, "")
|
|
if maxVerStr == "" {
|
|
maxVerStr = env.Get(EnvAPIObjectMaxVersionsLegacy, kvs.Get(apiObjectMaxVersions))
|
|
}
|
|
if maxVerStr != "" {
|
|
maxVersions, err := strconv.ParseInt(maxVerStr, 10, 64)
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
if maxVersions <= 0 {
|
|
return cfg, fmt.Errorf("invalid object max versions value: %v", maxVersions)
|
|
}
|
|
cfg.ObjectMaxVersions = maxVersions
|
|
} else {
|
|
cfg.ObjectMaxVersions = math.MaxInt64
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|