github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/config/api/api.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package api
    19  
    20  import (
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"math"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/minio/minio/internal/config"
    30  	"github.com/minio/pkg/v2/env"
    31  )
    32  
    33  // API sub-system constants
    34  const (
    35  	apiRequestsMax             = "requests_max"
    36  	apiRequestsDeadline        = "requests_deadline"
    37  	apiClusterDeadline         = "cluster_deadline"
    38  	apiCorsAllowOrigin         = "cors_allow_origin"
    39  	apiRemoteTransportDeadline = "remote_transport_deadline"
    40  	apiListQuorum              = "list_quorum"
    41  	apiReplicationPriority     = "replication_priority"
    42  	apiReplicationMaxWorkers   = "replication_max_workers"
    43  
    44  	apiTransitionWorkers           = "transition_workers"
    45  	apiStaleUploadsCleanupInterval = "stale_uploads_cleanup_interval"
    46  	apiStaleUploadsExpiry          = "stale_uploads_expiry"
    47  	apiDeleteCleanupInterval       = "delete_cleanup_interval"
    48  	apiDisableODirect              = "disable_odirect"
    49  	apiODirect                     = "odirect"
    50  	apiGzipObjects                 = "gzip_objects"
    51  	apiRootAccess                  = "root_access"
    52  	apiSyncEvents                  = "sync_events"
    53  	apiObjectMaxVersions           = "object_max_versions"
    54  
    55  	EnvAPIRequestsMax                 = "MINIO_API_REQUESTS_MAX"
    56  	EnvAPIRequestsDeadline            = "MINIO_API_REQUESTS_DEADLINE"
    57  	EnvAPIClusterDeadline             = "MINIO_API_CLUSTER_DEADLINE"
    58  	EnvAPICorsAllowOrigin             = "MINIO_API_CORS_ALLOW_ORIGIN"
    59  	EnvAPIRemoteTransportDeadline     = "MINIO_API_REMOTE_TRANSPORT_DEADLINE"
    60  	EnvAPITransitionWorkers           = "MINIO_API_TRANSITION_WORKERS"
    61  	EnvAPIListQuorum                  = "MINIO_API_LIST_QUORUM"
    62  	EnvAPISecureCiphers               = "MINIO_API_SECURE_CIPHERS" // default config.EnableOn
    63  	EnvAPIReplicationPriority         = "MINIO_API_REPLICATION_PRIORITY"
    64  	EnvAPIReplicationMaxWorkers       = "MINIO_API_REPLICATION_MAX_WORKERS"
    65  	EnvAPIStaleUploadsCleanupInterval = "MINIO_API_STALE_UPLOADS_CLEANUP_INTERVAL"
    66  	EnvAPIStaleUploadsExpiry          = "MINIO_API_STALE_UPLOADS_EXPIRY"
    67  	EnvAPIDeleteCleanupInterval       = "MINIO_API_DELETE_CLEANUP_INTERVAL"
    68  	EnvDeleteCleanupInterval          = "MINIO_DELETE_CLEANUP_INTERVAL"
    69  	EnvAPIODirect                     = "MINIO_API_ODIRECT"
    70  	EnvAPIDisableODirect              = "MINIO_API_DISABLE_ODIRECT"
    71  	EnvAPIGzipObjects                 = "MINIO_API_GZIP_OBJECTS"
    72  	EnvAPIRootAccess                  = "MINIO_API_ROOT_ACCESS" // default config.EnableOn
    73  	EnvAPISyncEvents                  = "MINIO_API_SYNC_EVENTS" // default "off"
    74  	EnvAPIObjectMaxVersions           = "MINIO_API_OBJECT_MAX_VERSIONS"
    75  	EnvAPIObjectMaxVersionsLegacy     = "_MINIO_OBJECT_MAX_VERSIONS"
    76  )
    77  
    78  // Deprecated key and ENVs
    79  const (
    80  	apiReadyDeadline            = "ready_deadline"
    81  	apiReplicationWorkers       = "replication_workers"
    82  	apiReplicationFailedWorkers = "replication_failed_workers"
    83  )
    84  
    85  // DefaultKVS - default storage class config
    86  var (
    87  	DefaultKVS = config.KVS{
    88  		config.KV{
    89  			Key:   apiRequestsMax,
    90  			Value: "0",
    91  		},
    92  		config.KV{
    93  			Key:   apiRequestsDeadline,
    94  			Value: "10s",
    95  		},
    96  		config.KV{
    97  			Key:   apiClusterDeadline,
    98  			Value: "10s",
    99  		},
   100  		config.KV{
   101  			Key:   apiCorsAllowOrigin,
   102  			Value: "*",
   103  		},
   104  		config.KV{
   105  			Key:   apiRemoteTransportDeadline,
   106  			Value: "2h",
   107  		},
   108  		config.KV{
   109  			Key:   apiListQuorum,
   110  			Value: "strict",
   111  		},
   112  		config.KV{
   113  			Key:   apiReplicationPriority,
   114  			Value: "auto",
   115  		},
   116  		config.KV{
   117  			Key:   apiReplicationMaxWorkers,
   118  			Value: "500",
   119  		},
   120  		config.KV{
   121  			Key:   apiTransitionWorkers,
   122  			Value: "100",
   123  		},
   124  		config.KV{
   125  			Key:   apiStaleUploadsCleanupInterval,
   126  			Value: "6h",
   127  		},
   128  		config.KV{
   129  			Key:   apiStaleUploadsExpiry,
   130  			Value: "24h",
   131  		},
   132  		config.KV{
   133  			Key:   apiDeleteCleanupInterval,
   134  			Value: "5m",
   135  		},
   136  		config.KV{
   137  			Key:           apiDisableODirect,
   138  			Value:         "",
   139  			HiddenIfEmpty: true,
   140  		},
   141  		config.KV{
   142  			Key:   apiODirect,
   143  			Value: config.EnableOn,
   144  		},
   145  		config.KV{
   146  			Key:   apiGzipObjects,
   147  			Value: config.EnableOff,
   148  		},
   149  		config.KV{
   150  			Key:   apiRootAccess,
   151  			Value: config.EnableOn,
   152  		},
   153  		config.KV{
   154  			Key:   apiSyncEvents,
   155  			Value: config.EnableOff,
   156  		},
   157  		config.KV{
   158  			Key:   apiObjectMaxVersions,
   159  			Value: "9223372036854775807",
   160  		},
   161  	}
   162  )
   163  
   164  // Config storage class configuration
   165  type Config struct {
   166  	RequestsMax                 int           `json:"requests_max"`
   167  	RequestsDeadline            time.Duration `json:"requests_deadline"`
   168  	ClusterDeadline             time.Duration `json:"cluster_deadline"`
   169  	CorsAllowOrigin             []string      `json:"cors_allow_origin"`
   170  	RemoteTransportDeadline     time.Duration `json:"remote_transport_deadline"`
   171  	ListQuorum                  string        `json:"list_quorum"`
   172  	ReplicationPriority         string        `json:"replication_priority"`
   173  	ReplicationMaxWorkers       int           `json:"replication_max_workers"`
   174  	TransitionWorkers           int           `json:"transition_workers"`
   175  	StaleUploadsCleanupInterval time.Duration `json:"stale_uploads_cleanup_interval"`
   176  	StaleUploadsExpiry          time.Duration `json:"stale_uploads_expiry"`
   177  	DeleteCleanupInterval       time.Duration `json:"delete_cleanup_interval"`
   178  	EnableODirect               bool          `json:"enable_odirect"`
   179  	GzipObjects                 bool          `json:"gzip_objects"`
   180  	RootAccess                  bool          `json:"root_access"`
   181  	SyncEvents                  bool          `json:"sync_events"`
   182  	ObjectMaxVersions           int64         `json:"object_max_versions"`
   183  }
   184  
   185  // UnmarshalJSON - Validate SS and RRS parity when unmarshalling JSON.
   186  func (sCfg *Config) UnmarshalJSON(data []byte) error {
   187  	type Alias Config
   188  	aux := &struct {
   189  		*Alias
   190  	}{
   191  		Alias: (*Alias)(sCfg),
   192  	}
   193  	return json.Unmarshal(data, &aux)
   194  }
   195  
   196  // LookupConfig - lookup api config and override with valid environment settings if any.
   197  func LookupConfig(kvs config.KVS) (cfg Config, err error) {
   198  	deprecatedKeys := []string{
   199  		apiReadyDeadline,
   200  		"extend_list_cache_life",
   201  		apiReplicationWorkers,
   202  		apiReplicationFailedWorkers,
   203  		"expiry_workers",
   204  	}
   205  
   206  	disableODirect := env.Get(EnvAPIDisableODirect, kvs.Get(apiDisableODirect)) == config.EnableOn
   207  	enableODirect := env.Get(EnvAPIODirect, kvs.Get(apiODirect)) == config.EnableOn
   208  	gzipObjects := env.Get(EnvAPIGzipObjects, kvs.Get(apiGzipObjects)) == config.EnableOn
   209  	rootAccess := env.Get(EnvAPIRootAccess, kvs.Get(apiRootAccess)) == config.EnableOn
   210  
   211  	cfg = Config{
   212  		EnableODirect: enableODirect || !disableODirect,
   213  		GzipObjects:   gzipObjects,
   214  		RootAccess:    rootAccess,
   215  	}
   216  
   217  	var corsAllowOrigin []string
   218  	corsList := env.Get(EnvAPICorsAllowOrigin, kvs.Get(apiCorsAllowOrigin))
   219  	if corsList == "" {
   220  		corsAllowOrigin = []string{"*"} // defaults to '*'
   221  	} else {
   222  		corsAllowOrigin = strings.Split(corsList, ",")
   223  		for _, cors := range corsAllowOrigin {
   224  			if cors == "" {
   225  				return cfg, errors.New("invalid cors value")
   226  			}
   227  		}
   228  	}
   229  	cfg.CorsAllowOrigin = corsAllowOrigin
   230  
   231  	if err = config.CheckValidKeys(config.APISubSys, kvs, DefaultKVS, deprecatedKeys...); err != nil {
   232  		return cfg, err
   233  	}
   234  
   235  	// Check environment variables parameters
   236  	requestsMax, err := strconv.Atoi(env.Get(EnvAPIRequestsMax, kvs.GetWithDefault(apiRequestsMax, DefaultKVS)))
   237  	if err != nil {
   238  		return cfg, err
   239  	}
   240  
   241  	cfg.RequestsMax = requestsMax
   242  	if requestsMax < 0 {
   243  		return cfg, errors.New("invalid API max requests value")
   244  	}
   245  
   246  	requestsDeadline, err := time.ParseDuration(env.Get(EnvAPIRequestsDeadline, kvs.GetWithDefault(apiRequestsDeadline, DefaultKVS)))
   247  	if err != nil {
   248  		return cfg, err
   249  	}
   250  	cfg.RequestsDeadline = requestsDeadline
   251  
   252  	clusterDeadline, err := time.ParseDuration(env.Get(EnvAPIClusterDeadline, kvs.GetWithDefault(apiClusterDeadline, DefaultKVS)))
   253  	if err != nil {
   254  		return cfg, err
   255  	}
   256  	cfg.ClusterDeadline = clusterDeadline
   257  
   258  	remoteTransportDeadline, err := time.ParseDuration(env.Get(EnvAPIRemoteTransportDeadline, kvs.GetWithDefault(apiRemoteTransportDeadline, DefaultKVS)))
   259  	if err != nil {
   260  		return cfg, err
   261  	}
   262  	cfg.RemoteTransportDeadline = remoteTransportDeadline
   263  
   264  	listQuorum := env.Get(EnvAPIListQuorum, kvs.GetWithDefault(apiListQuorum, DefaultKVS))
   265  	switch listQuorum {
   266  	case "strict", "optimal", "reduced", "disk", "auto":
   267  	default:
   268  		return cfg, fmt.Errorf("invalid value %v for list_quorum: will default to 'strict'", listQuorum)
   269  	}
   270  	cfg.ListQuorum = listQuorum
   271  
   272  	replicationPriority := env.Get(EnvAPIReplicationPriority, kvs.GetWithDefault(apiReplicationPriority, DefaultKVS))
   273  	switch replicationPriority {
   274  	case "slow", "fast", "auto":
   275  	default:
   276  		return cfg, fmt.Errorf("invalid value %v for replication_priority", replicationPriority)
   277  	}
   278  	cfg.ReplicationPriority = replicationPriority
   279  	replicationMaxWorkers, err := strconv.Atoi(env.Get(EnvAPIReplicationMaxWorkers, kvs.GetWithDefault(apiReplicationMaxWorkers, DefaultKVS)))
   280  	if err != nil {
   281  		return cfg, err
   282  	}
   283  
   284  	if replicationMaxWorkers <= 0 || replicationMaxWorkers > 500 {
   285  		return cfg, config.ErrInvalidReplicationWorkersValue(nil).Msg("Number of replication workers should be between 1 and 500")
   286  	}
   287  	cfg.ReplicationMaxWorkers = replicationMaxWorkers
   288  	transitionWorkers, err := strconv.Atoi(env.Get(EnvAPITransitionWorkers, kvs.GetWithDefault(apiTransitionWorkers, DefaultKVS)))
   289  	if err != nil {
   290  		return cfg, err
   291  	}
   292  	cfg.TransitionWorkers = transitionWorkers
   293  
   294  	v := env.Get(EnvAPIDeleteCleanupInterval, kvs.Get(apiDeleteCleanupInterval))
   295  	if v == "" {
   296  		v = env.Get(EnvDeleteCleanupInterval, kvs.GetWithDefault(apiDeleteCleanupInterval, DefaultKVS))
   297  	}
   298  
   299  	deleteCleanupInterval, err := time.ParseDuration(v)
   300  	if err != nil {
   301  		return cfg, err
   302  	}
   303  	cfg.DeleteCleanupInterval = deleteCleanupInterval
   304  
   305  	staleUploadsCleanupInterval, err := time.ParseDuration(env.Get(EnvAPIStaleUploadsCleanupInterval, kvs.GetWithDefault(apiStaleUploadsCleanupInterval, DefaultKVS)))
   306  	if err != nil {
   307  		return cfg, err
   308  	}
   309  	cfg.StaleUploadsCleanupInterval = staleUploadsCleanupInterval
   310  
   311  	staleUploadsExpiry, err := time.ParseDuration(env.Get(EnvAPIStaleUploadsExpiry, kvs.GetWithDefault(apiStaleUploadsExpiry, DefaultKVS)))
   312  	if err != nil {
   313  		return cfg, err
   314  	}
   315  	cfg.StaleUploadsExpiry = staleUploadsExpiry
   316  
   317  	cfg.SyncEvents = env.Get(EnvAPISyncEvents, kvs.Get(apiSyncEvents)) == config.EnableOn
   318  
   319  	maxVerStr := env.Get(EnvAPIObjectMaxVersions, "")
   320  	if maxVerStr == "" {
   321  		maxVerStr = env.Get(EnvAPIObjectMaxVersionsLegacy, kvs.Get(apiObjectMaxVersions))
   322  	}
   323  	if maxVerStr != "" {
   324  		maxVersions, err := strconv.ParseInt(maxVerStr, 10, 64)
   325  		if err != nil {
   326  			return cfg, err
   327  		}
   328  		if maxVersions <= 0 {
   329  			return cfg, fmt.Errorf("invalid object max versions value: %v", maxVersions)
   330  		}
   331  		cfg.ObjectMaxVersions = maxVersions
   332  	} else {
   333  		cfg.ObjectMaxVersions = math.MaxInt64
   334  	}
   335  
   336  	return cfg, nil
   337  }