allow retries for bucket encryption/policy quorum reloads (#9513)

We should allow quorum errors to be send upwards
such that caller can retry while reading bucket
encryption/policy configs when server is starting
up, this allows distributed setups to load the
configuration properly.

Current code didn't facilitate this and would have
never loaded the actual configs during rolling,
server restarts.
This commit is contained in:
Harshavardhana 2020-05-04 09:42:58 -07:00 committed by GitHub
parent 3e063cca5c
commit 9b3b04ecec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 132 additions and 109 deletions

View File

@ -18,6 +18,7 @@ package cmd
import ( import (
"encoding/xml" "encoding/xml"
"fmt"
"io" "io"
"net/http" "net/http"
@ -69,7 +70,12 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r
// Parse bucket encryption xml // Parse bucket encryption xml
encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize)) encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize))
if err != nil { if err != nil {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r)) apiErr := APIError{
Code: "MalformedXML",
Description: fmt.Sprintf("%s (%s)", errorCodes[ErrMalformedXML].Description, err),
HTTPStatusCode: errorCodes[ErrMalformedXML].HTTPStatusCode,
}
writeErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
return return
} }
@ -86,7 +92,7 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r
} }
// Update the in-memory bucket encryption cache // Update the in-memory bucket encryption cache
globalBucketSSEConfigSys.Set(bucket, *encConfig) globalBucketSSEConfigSys.Set(bucket, encConfig)
// Update peer MinIO servers of the updated bucket encryption config // Update peer MinIO servers of the updated bucket encryption config
globalNotificationSys.SetBucketSSEConfig(ctx, bucket, encConfig) globalNotificationSys.SetBucketSSEConfig(ctx, bucket, encConfig)
@ -174,6 +180,7 @@ func (api objectAPIHandlers) DeleteBucketEncryptionHandler(w http.ResponseWriter
// Remove entry from the in-memory bucket encryption cache // Remove entry from the in-memory bucket encryption cache
globalBucketSSEConfigSys.Remove(bucket) globalBucketSSEConfigSys.Remove(bucket)
// Update peer MinIO servers of the updated bucket encryption config // Update peer MinIO servers of the updated bucket encryption config
globalNotificationSys.RemoveBucketSSEConfig(ctx, bucket) globalNotificationSys.RemoveBucketSSEConfig(ctx, bucket)

View File

@ -31,13 +31,13 @@ import (
// BucketSSEConfigSys - in-memory cache of bucket encryption config // BucketSSEConfigSys - in-memory cache of bucket encryption config
type BucketSSEConfigSys struct { type BucketSSEConfigSys struct {
sync.RWMutex sync.RWMutex
bucketSSEConfigMap map[string]bucketsse.BucketSSEConfig bucketSSEConfigMap map[string]*bucketsse.BucketSSEConfig
} }
// NewBucketSSEConfigSys - Creates an empty in-memory bucket encryption configuration cache // NewBucketSSEConfigSys - Creates an empty in-memory bucket encryption configuration cache
func NewBucketSSEConfigSys() *BucketSSEConfigSys { func NewBucketSSEConfigSys() *BucketSSEConfigSys {
return &BucketSSEConfigSys{ return &BucketSSEConfigSys{
bucketSSEConfigMap: make(map[string]bucketsse.BucketSSEConfig), bucketSSEConfigMap: make(map[string]*bucketsse.BucketSSEConfig),
} }
} }
@ -47,11 +47,12 @@ func (sys *BucketSSEConfigSys) load(buckets []BucketInfo, objAPI ObjectLayer) er
config, err := objAPI.GetBucketSSEConfig(GlobalContext, bucket.Name) config, err := objAPI.GetBucketSSEConfig(GlobalContext, bucket.Name)
if err != nil { if err != nil {
if _, ok := err.(BucketSSEConfigNotFound); ok { if _, ok := err.(BucketSSEConfigNotFound); ok {
sys.Remove(bucket.Name)
}
continue continue
} }
sys.Set(bucket.Name, *config) // Quorum errors should be returned.
return err
}
sys.Set(bucket.Name, config)
} }
return nil return nil
@ -73,7 +74,7 @@ func (sys *BucketSSEConfigSys) Init(buckets []BucketInfo, objAPI ObjectLayer) er
} }
// Get - gets bucket encryption config for the given bucket. // Get - gets bucket encryption config for the given bucket.
func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSEConfig, ok bool) { func (sys *BucketSSEConfigSys) Get(bucket string) (config *bucketsse.BucketSSEConfig, ok bool) {
// We don't cache bucket encryption config in gateway mode. // We don't cache bucket encryption config in gateway mode.
if globalIsGateway { if globalIsGateway {
objAPI := newObjectLayerWithoutSafeModeFn() objAPI := newObjectLayerWithoutSafeModeFn()
@ -85,7 +86,7 @@ func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSECon
if err != nil { if err != nil {
return return
} }
return *cfg, true return cfg, true
} }
sys.Lock() sys.Lock()
@ -95,7 +96,7 @@ func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSECon
} }
// Set - sets bucket encryption config to given bucket name. // Set - sets bucket encryption config to given bucket name.
func (sys *BucketSSEConfigSys) Set(bucket string, config bucketsse.BucketSSEConfig) { func (sys *BucketSSEConfigSys) Set(bucket string, config *bucketsse.BucketSSEConfig) {
// We don't cache bucket encryption config in gateway mode. // We don't cache bucket encryption config in gateway mode.
if globalIsGateway { if globalIsGateway {
return return
@ -146,8 +147,8 @@ func removeBucketSSEConfig(ctx context.Context, objAPI ObjectLayer, bucket strin
// Path to bucket-encryption.xml for the given bucket. // Path to bucket-encryption.xml for the given bucket.
configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig) configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig)
if err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile); err != nil { if err := deleteConfig(ctx, objAPI, configFile); err != nil {
if _, ok := err.(ObjectNotFound); ok { if err == errConfigNotFound {
return BucketSSEConfigNotFound{Bucket: bucket} return BucketSSEConfigNotFound{Bucket: bucket}
} }
return err return err

View File

@ -589,8 +589,9 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
return return
} }
if objectLockEnabled { if objectLockEnabled {
globalBucketObjectLockConfig.Set(bucket, objectlock.Retention{}) ret := &objectlock.Retention{}
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, objectlock.Retention{}) globalBucketObjectLockConfig.Set(bucket, ret)
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, ret)
} }
} }
@ -1069,8 +1070,8 @@ func (api objectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWri
globalBucketObjectLockConfig.Set(bucket, retention) globalBucketObjectLockConfig.Set(bucket, retention)
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, retention) globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, retention)
} else { } else {
globalBucketObjectLockConfig.Remove(bucket) globalBucketObjectLockConfig.Set(bucket, &objectlock.Retention{})
globalNotificationSys.RemoveBucketObjectLockConfig(ctx, bucket) globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, &objectlock.Retention{})
} }
// Write success response. // Write success response.

View File

@ -77,7 +77,7 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r
return return
} }
globalLifecycleSys.Set(bucket, *bucketLifecycle) globalLifecycleSys.Set(bucket, bucketLifecycle)
globalNotificationSys.SetBucketLifecycle(ctx, bucket, bucketLifecycle) globalNotificationSys.SetBucketLifecycle(ctx, bucket, bucketLifecycle)
// Success. // Success.

View File

@ -35,11 +35,11 @@ const (
// LifecycleSys - Bucket lifecycle subsystem. // LifecycleSys - Bucket lifecycle subsystem.
type LifecycleSys struct { type LifecycleSys struct {
sync.RWMutex sync.RWMutex
bucketLifecycleMap map[string]lifecycle.Lifecycle bucketLifecycleMap map[string]*lifecycle.Lifecycle
} }
// Set - sets lifecycle config to given bucket name. // Set - sets lifecycle config to given bucket name.
func (sys *LifecycleSys) Set(bucketName string, lifecycle lifecycle.Lifecycle) { func (sys *LifecycleSys) Set(bucketName string, lifecycle *lifecycle.Lifecycle) {
if globalIsGateway { if globalIsGateway {
// no-op // no-op
return return
@ -52,7 +52,7 @@ func (sys *LifecycleSys) Set(bucketName string, lifecycle lifecycle.Lifecycle) {
} }
// Get - gets lifecycle config associated to a given bucket name. // Get - gets lifecycle config associated to a given bucket name.
func (sys *LifecycleSys) Get(bucketName string) (lc lifecycle.Lifecycle, ok bool) { func (sys *LifecycleSys) Get(bucketName string) (lc *lifecycle.Lifecycle, ok bool) {
if globalIsGateway { if globalIsGateway {
// When gateway is enabled, no cached value // When gateway is enabled, no cached value
// is used to validate life cycle policies. // is used to validate life cycle policies.
@ -65,7 +65,7 @@ func (sys *LifecycleSys) Get(bucketName string) (lc lifecycle.Lifecycle, ok bool
if err != nil { if err != nil {
return return
} }
return *l, true return l, true
} }
sys.Lock() sys.Lock()
@ -104,8 +104,8 @@ func removeLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName s
// Construct path to lifecycle.xml for the given bucket. // Construct path to lifecycle.xml for the given bucket.
configFile := path.Join(bucketConfigPrefix, bucketName, bucketLifecycleConfig) configFile := path.Join(bucketConfigPrefix, bucketName, bucketLifecycleConfig)
if err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile); err != nil { if err := deleteConfig(ctx, objAPI, configFile); err != nil {
if _, ok := err.(ObjectNotFound); ok { if err == errConfigNotFound {
return BucketLifecycleNotFound{Bucket: bucketName} return BucketLifecycleNotFound{Bucket: bucketName}
} }
return err return err
@ -116,7 +116,7 @@ func removeLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName s
// NewLifecycleSys - creates new lifecycle system. // NewLifecycleSys - creates new lifecycle system.
func NewLifecycleSys() *LifecycleSys { func NewLifecycleSys() *LifecycleSys {
return &LifecycleSys{ return &LifecycleSys{
bucketLifecycleMap: make(map[string]lifecycle.Lifecycle), bucketLifecycleMap: make(map[string]*lifecycle.Lifecycle),
} }
} }
@ -139,15 +139,16 @@ func (sys *LifecycleSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
// Loads lifecycle policies for all buckets into LifecycleSys. // Loads lifecycle policies for all buckets into LifecycleSys.
func (sys *LifecycleSys) load(buckets []BucketInfo, objAPI ObjectLayer) error { func (sys *LifecycleSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
for _, bucket := range buckets { for _, bucket := range buckets {
config, err := objAPI.GetBucketLifecycle(GlobalContext, bucket.Name) config, err := getLifecycleConfig(objAPI, bucket.Name)
if err != nil { if err != nil {
if _, ok := err.(BucketLifecycleNotFound); ok { if _, ok := err.(BucketLifecycleNotFound); ok {
sys.Remove(bucket.Name)
}
continue continue
} }
// Quorum errors should be returned.
return err
}
sys.Set(bucket.Name, *config) sys.Set(bucket.Name, config)
} }
return nil return nil

View File

@ -398,7 +398,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error
configData, err := readConfig(ctx, objAPI, configFile) configData, err := readConfig(ctx, objAPI, configFile)
if err != nil { if err != nil {
if errors.Is(err, errConfigNotFound) { if errors.Is(err, errConfigNotFound) {
globalBucketObjectLockConfig.Set(bucket.Name, objectlock.Retention{}) globalBucketObjectLockConfig.Set(bucket.Name, &objectlock.Retention{})
continue continue
} }
return err return err
@ -408,7 +408,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error
if err != nil { if err != nil {
return err return err
} }
retention := objectlock.Retention{} retention := &objectlock.Retention{}
if config.Rule != nil { if config.Rule != nil {
retention = config.ToRetention() retention = config.ToRetention()
} }

View File

@ -92,7 +92,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
return return
} }
globalPolicySys.Set(bucket, *bucketPolicy) globalPolicySys.Set(bucket, bucketPolicy)
globalNotificationSys.SetBucketPolicy(ctx, bucket, bucketPolicy) globalNotificationSys.SetBucketPolicy(ctx, bucket, bucketPolicy)
// Success. // Success.

View File

@ -39,11 +39,11 @@ import (
// PolicySys - policy subsystem. // PolicySys - policy subsystem.
type PolicySys struct { type PolicySys struct {
sync.RWMutex sync.RWMutex
bucketPolicyMap map[string]policy.Policy bucketPolicyMap map[string]*policy.Policy
} }
// Set - sets policy to given bucket name. If policy is empty, existing policy is removed. // Set - sets policy to given bucket name. If policy is empty, existing policy is removed.
func (sys *PolicySys) Set(bucketName string, policy policy.Policy) { func (sys *PolicySys) Set(bucketName string, policy *policy.Policy) {
if globalIsGateway { if globalIsGateway {
// Set policy is a non-op under gateway mode. // Set policy is a non-op under gateway mode.
return return
@ -97,13 +97,13 @@ func (sys *PolicySys) IsAllowed(args policy.Args) bool {
// Loads policies for all buckets into PolicySys. // Loads policies for all buckets into PolicySys.
func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error { func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
for _, bucket := range buckets { for _, bucket := range buckets {
config, err := objAPI.GetBucketPolicy(GlobalContext, bucket.Name) config, err := getPolicyConfig(objAPI, bucket.Name)
if err != nil { if err != nil {
if _, ok := err.(BucketPolicyNotFound); ok { if _, ok := err.(BucketPolicyNotFound); ok {
sys.Remove(bucket.Name)
}
continue continue
} }
return err
}
// This part is specifically written to handle migration // This part is specifically written to handle migration
// when the Version string is empty, this was allowed // when the Version string is empty, this was allowed
// in all previous minio releases but we need to migrate // in all previous minio releases but we need to migrate
@ -118,7 +118,7 @@ func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
return err return err
} }
} }
sys.Set(bucket.Name, *config) sys.Set(bucket.Name, config)
} }
return nil return nil
} }
@ -126,7 +126,7 @@ func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
// Init - initializes policy system from policy.json of all buckets. // Init - initializes policy system from policy.json of all buckets.
func (sys *PolicySys) Init(buckets []BucketInfo, objAPI ObjectLayer) error { func (sys *PolicySys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
if objAPI == nil { if objAPI == nil {
return errInvalidArgument return errServerNotInitialized
} }
// In gateway mode, we don't need to load the policies // In gateway mode, we don't need to load the policies
@ -142,7 +142,7 @@ func (sys *PolicySys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
// NewPolicySys - creates new policy system. // NewPolicySys - creates new policy system.
func NewPolicySys() *PolicySys { func NewPolicySys() *PolicySys {
return &PolicySys{ return &PolicySys{
bucketPolicyMap: make(map[string]policy.Policy), bucketPolicyMap: make(map[string]*policy.Policy),
} }
} }

View File

@ -60,23 +60,50 @@ func (sys *BucketQuotaSys) Remove(bucketName string) {
sys.Unlock() sys.Unlock()
} }
// Exists - bucketName has Quota config set // Buckets - list of buckets with quota configuration
func (sys *BucketQuotaSys) Exists(bucketName string) bool { func (sys *BucketQuotaSys) Buckets() []string {
sys.RLock()
_, ok := sys.quotaMap[bucketName]
sys.RUnlock()
return ok
}
// Keys - list of buckets with quota configuration
func (sys *BucketQuotaSys) Keys() []string {
sys.RLock() sys.RLock()
defer sys.RUnlock() defer sys.RUnlock()
var keys []string var buckets []string
for k := range sys.quotaMap { for k := range sys.quotaMap {
keys = append(keys, k) buckets = append(buckets, k)
} }
return keys return buckets
}
// Init initialize bucket quota sys configuration with all buckets.
func (sys *BucketQuotaSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
if objAPI == nil {
return errServerNotInitialized
}
// In gateway mode, we always fetch the bucket lifecycle configuration from the gateway backend.
// So, this is a no-op for gateway servers.
if globalIsGateway {
return nil
}
return sys.load(buckets, objAPI)
}
func (sys *BucketQuotaSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
for _, bucket := range buckets {
ctx := logger.SetReqInfo(GlobalContext, &logger.ReqInfo{BucketName: bucket.Name})
configFile := path.Join(bucketConfigPrefix, bucket.Name, bucketQuotaConfigFile)
configData, err := readConfig(ctx, objAPI, configFile)
if err != nil {
if errors.Is(err, errConfigNotFound) {
continue
}
return err
}
quotaCfg, err := parseBucketQuota(configData)
if err != nil {
return err
}
globalBucketQuotaSys.Set(bucket.Name, quotaCfg)
}
return nil
} }
// NewBucketQuotaSys returns initialized BucketQuotaSys // NewBucketQuotaSys returns initialized BucketQuotaSys
@ -131,26 +158,6 @@ func enforceBucketQuota(ctx context.Context, bucket string, size int64) error {
return globalBucketStorageCache.check(ctx, q, bucket, size) return globalBucketStorageCache.check(ctx, q, bucket, size)
} }
func initBucketQuotaSys(buckets []BucketInfo, objAPI ObjectLayer) error {
for _, bucket := range buckets {
ctx := logger.SetReqInfo(GlobalContext, &logger.ReqInfo{BucketName: bucket.Name})
configFile := path.Join(bucketConfigPrefix, bucket.Name, bucketQuotaConfigFile)
configData, err := readConfig(ctx, objAPI, configFile)
if err != nil {
if errors.Is(err, errConfigNotFound) {
continue
}
return err
}
quotaCfg, err := parseBucketQuota(configData)
if err != nil {
return err
}
globalBucketQuotaSys.Set(bucket.Name, quotaCfg)
}
return nil
}
const ( const (
bgQuotaInterval = 1 * time.Hour bgQuotaInterval = 1 * time.Hour
) )
@ -182,7 +189,7 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) error {
if env.Get(envDataUsageCrawlConf, config.EnableOn) == config.EnableOff { if env.Get(envDataUsageCrawlConf, config.EnableOn) == config.EnableOff {
return nil return nil
} }
for _, bucket := range globalBucketQuotaSys.Keys() { for _, bucket := range globalBucketQuotaSys.Buckets() {
// Check if the current bucket has quota restrictions, if not skip it // Check if the current bucket has quota restrictions, if not skip it
cfg, ok := globalBucketQuotaSys.Get(bucket) cfg, ok := globalBucketQuotaSys.Get(bucket)
if !ok { if !ok {

View File

@ -218,7 +218,7 @@ var (
globalBucketObjectLockConfig = objectlock.NewBucketObjectLockConfig() globalBucketObjectLockConfig = objectlock.NewBucketObjectLockConfig()
globalBucketQuotaSys = NewBucketQuotaSys() globalBucketQuotaSys *BucketQuotaSys
globalBucketStorageCache bucketStorageCache globalBucketStorageCache bucketStorageCache
// Disk cache drives // Disk cache drives

View File

@ -771,7 +771,7 @@ func (sys *NotificationSys) load(buckets []BucketInfo, objAPI ObjectLayer) error
// Init - initializes notification system from notification.xml and listener.json of all buckets. // Init - initializes notification system from notification.xml and listener.json of all buckets.
func (sys *NotificationSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error { func (sys *NotificationSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
if objAPI == nil { if objAPI == nil {
return errInvalidArgument return errServerNotInitialized
} }
// In gateway mode, notifications are not supported. // In gateway mode, notifications are not supported.
@ -905,7 +905,7 @@ func (sys *NotificationSys) Send(args eventArgs) {
} }
// PutBucketObjectLockConfig - put bucket object lock configuration to all peers. // PutBucketObjectLockConfig - put bucket object lock configuration to all peers.
func (sys *NotificationSys) PutBucketObjectLockConfig(ctx context.Context, bucketName string, retention objectlock.Retention) { func (sys *NotificationSys) PutBucketObjectLockConfig(ctx context.Context, bucketName string, retention *objectlock.Retention) {
g := errgroup.WithNErrs(len(sys.peerClients)) g := errgroup.WithNErrs(len(sys.peerClients))
for index, client := range sys.peerClients { for index, client := range sys.peerClients {
if client == nil { if client == nil {

View File

@ -656,12 +656,12 @@ func (client *peerRESTClient) PutBucketNotification(bucket string, rulesMap even
} }
// PutBucketObjectLockConfig - PUT bucket object lock configuration. // PutBucketObjectLockConfig - PUT bucket object lock configuration.
func (client *peerRESTClient) PutBucketObjectLockConfig(bucket string, retention objectlock.Retention) error { func (client *peerRESTClient) PutBucketObjectLockConfig(bucket string, retention *objectlock.Retention) error {
values := make(url.Values) values := make(url.Values)
values.Set(peerRESTBucket, bucket) values.Set(peerRESTBucket, bucket)
var reader bytes.Buffer var reader bytes.Buffer
err := gob.NewEncoder(&reader).Encode(&retention) err := gob.NewEncoder(&reader).Encode(retention)
if err != nil { if err != nil {
return err return err
} }

View File

@ -675,13 +675,14 @@ func (s *peerRESTServer) SetBucketPolicyHandler(w http.ResponseWriter, r *http.R
s.writeErrorResponse(w, errors.New("Bucket name is missing")) s.writeErrorResponse(w, errors.New("Bucket name is missing"))
return return
} }
var policyData policy.Policy
if r.ContentLength < 0 { if r.ContentLength < 0 {
s.writeErrorResponse(w, errInvalidArgument) s.writeErrorResponse(w, errInvalidArgument)
return return
} }
err := gob.NewDecoder(r.Body).Decode(&policyData) var policyData = &policy.Policy{}
err := gob.NewDecoder(r.Body).Decode(policyData)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
@ -716,13 +717,13 @@ func (s *peerRESTServer) SetBucketLifecycleHandler(w http.ResponseWriter, r *htt
s.writeErrorResponse(w, errors.New("Bucket name is missing")) s.writeErrorResponse(w, errors.New("Bucket name is missing"))
return return
} }
var lifecycleData lifecycle.Lifecycle
if r.ContentLength < 0 { if r.ContentLength < 0 {
s.writeErrorResponse(w, errInvalidArgument) s.writeErrorResponse(w, errInvalidArgument)
return return
} }
err := gob.NewDecoder(r.Body).Decode(&lifecycleData) var lifecycleData = &lifecycle.Lifecycle{}
err := gob.NewDecoder(r.Body).Decode(lifecycleData)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
@ -758,13 +759,13 @@ func (s *peerRESTServer) SetBucketSSEConfigHandler(w http.ResponseWriter, r *htt
return return
} }
var encConfig bucketsse.BucketSSEConfig
if r.ContentLength < 0 { if r.ContentLength < 0 {
s.writeErrorResponse(w, errInvalidArgument) s.writeErrorResponse(w, errInvalidArgument)
return return
} }
err := gob.NewDecoder(r.Body).Decode(&encConfig) var encConfig = &bucketsse.BucketSSEConfig{}
err := gob.NewDecoder(r.Body).Decode(encConfig)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
@ -860,13 +861,13 @@ func (s *peerRESTServer) PutBucketObjectLockConfigHandler(w http.ResponseWriter,
return return
} }
var retention objectlock.Retention
if r.ContentLength < 0 { if r.ContentLength < 0 {
s.writeErrorResponse(w, errInvalidArgument) s.writeErrorResponse(w, errInvalidArgument)
return return
} }
err := gob.NewDecoder(r.Body).Decode(&retention) var retention = &objectlock.Retention{}
err := gob.NewDecoder(r.Body).Decode(retention)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return

View File

@ -41,10 +41,10 @@ func TestPolicySysSet(t *testing.T) {
}, },
} }
case1Result := NewPolicySys() case1Result := NewPolicySys()
case1Result.bucketPolicyMap["mybucket"] = case1Policy case1Result.bucketPolicyMap["mybucket"] = &case1Policy
case2PolicySys := NewPolicySys() case2PolicySys := NewPolicySys()
case2PolicySys.bucketPolicyMap["mybucket"] = case1Policy case2PolicySys.bucketPolicyMap["mybucket"] = &case1Policy
case2Policy := policy.Policy{ case2Policy := policy.Policy{
Version: policy.DefaultVersion, Version: policy.DefaultVersion,
Statements: []policy.Statement{ Statements: []policy.Statement{
@ -58,10 +58,10 @@ func TestPolicySysSet(t *testing.T) {
}, },
} }
case2Result := NewPolicySys() case2Result := NewPolicySys()
case2Result.bucketPolicyMap["mybucket"] = case2Policy case2Result.bucketPolicyMap["mybucket"] = &case2Policy
case3PolicySys := NewPolicySys() case3PolicySys := NewPolicySys()
case3PolicySys.bucketPolicyMap["mybucket"] = case2Policy case3PolicySys.bucketPolicyMap["mybucket"] = &case2Policy
case3Policy := policy.Policy{ case3Policy := policy.Policy{
ID: "MyPolicyForMyBucket", ID: "MyPolicyForMyBucket",
Version: policy.DefaultVersion, Version: policy.DefaultVersion,
@ -81,7 +81,7 @@ func TestPolicySysSet(t *testing.T) {
for i, testCase := range testCases { for i, testCase := range testCases {
result := testCase.policySys result := testCase.policySys
result.Set(testCase.bucketName, testCase.bucketPolicy) result.Set(testCase.bucketName, &testCase.bucketPolicy)
if !reflect.DeepEqual(result, testCase.expectedResult) { if !reflect.DeepEqual(result, testCase.expectedResult) {
t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result)
@ -103,7 +103,7 @@ func TestPolicySysRemove(t *testing.T) {
}, },
} }
case1PolicySys := NewPolicySys() case1PolicySys := NewPolicySys()
case1PolicySys.bucketPolicyMap["mybucket"] = case1Policy case1PolicySys.bucketPolicyMap["mybucket"] = &case1Policy
case1Result := NewPolicySys() case1Result := NewPolicySys()
case2Policy := policy.Policy{ case2Policy := policy.Policy{
@ -119,9 +119,9 @@ func TestPolicySysRemove(t *testing.T) {
}, },
} }
case2PolicySys := NewPolicySys() case2PolicySys := NewPolicySys()
case2PolicySys.bucketPolicyMap["mybucket"] = case2Policy case2PolicySys.bucketPolicyMap["mybucket"] = &case2Policy
case2Result := NewPolicySys() case2Result := NewPolicySys()
case2Result.bucketPolicyMap["mybucket"] = case2Policy case2Result.bucketPolicyMap["mybucket"] = &case2Policy
case3PolicySys := NewPolicySys() case3PolicySys := NewPolicySys()
case3Result := NewPolicySys() case3Result := NewPolicySys()
@ -148,7 +148,7 @@ func TestPolicySysRemove(t *testing.T) {
func TestPolicySysIsAllowed(t *testing.T) { func TestPolicySysIsAllowed(t *testing.T) {
policySys := NewPolicySys() policySys := NewPolicySys()
policySys.Set("mybucket", policy.Policy{ policySys.Set("mybucket", &policy.Policy{
Version: policy.DefaultVersion, Version: policy.DefaultVersion,
Statements: []policy.Statement{ Statements: []policy.Statement{
policy.NewStatement( policy.NewStatement(

View File

@ -155,6 +155,9 @@ func newAllSubsystems() {
// Create new bucket encryption subsystem // Create new bucket encryption subsystem
globalBucketSSEConfigSys = NewBucketSSEConfigSys() globalBucketSSEConfigSys = NewBucketSSEConfigSys()
// Create new bucket quota subsystem
globalBucketQuotaSys = NewBucketQuotaSys()
} }
func initSafeMode(buckets []BucketInfo) (err error) { func initSafeMode(buckets []BucketInfo) (err error) {
@ -275,10 +278,6 @@ func initAllSubsystems(buckets []BucketInfo, newObject ObjectLayer) (err error)
if err = initBucketObjectLockConfig(buckets, newObject); err != nil { if err = initBucketObjectLockConfig(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize object lock system: %w", err) return fmt.Errorf("Unable to initialize object lock system: %w", err)
} }
// Initialize bucket quota system.
if err = initBucketQuotaSys(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize bucket quota system: %w", err)
}
// Initialize lifecycle system. // Initialize lifecycle system.
if err = globalLifecycleSys.Init(buckets, newObject); err != nil { if err = globalLifecycleSys.Init(buckets, newObject); err != nil {
@ -290,6 +289,11 @@ func initAllSubsystems(buckets []BucketInfo, newObject ObjectLayer) (err error)
return fmt.Errorf("Unable to initialize bucket encryption subsystem: %w", err) return fmt.Errorf("Unable to initialize bucket encryption subsystem: %w", err)
} }
// Initialize bucket quota system.
if err = globalBucketQuotaSys.Init(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize bucket quota system: %w", err)
}
return nil return nil
} }

View File

@ -1750,7 +1750,7 @@ func ExecObjectLayerAPIAnonTest(t *testing.T, obj ObjectLayer, testName, bucketN
if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil {
t.Fatalf("unexpected error. %v", err) t.Fatalf("unexpected error. %v", err)
} }
globalPolicySys.Set(bucketName, *bucketPolicy) globalPolicySys.Set(bucketName, bucketPolicy)
defer globalPolicySys.Remove(bucketName) defer globalPolicySys.Remove(bucketName)
// now call the handler again with the unsigned/anonymous request, it should be accepted. // now call the handler again with the unsigned/anonymous request, it should be accepted.

View File

@ -1904,7 +1904,7 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
return toJSONError(ctx, err, args.BucketName) return toJSONError(ctx, err, args.BucketName)
} }
globalPolicySys.Set(args.BucketName, *bucketPolicy) globalPolicySys.Set(args.BucketName, bucketPolicy)
globalNotificationSys.SetBucketPolicy(ctx, args.BucketName, bucketPolicy) globalNotificationSys.SetBucketPolicy(ctx, args.BucketName, bucketPolicy)
} }

View File

@ -548,7 +548,7 @@ func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHa
if err = obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { if err = obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil {
t.Fatalf("unexpected error. %v", err) t.Fatalf("unexpected error. %v", err)
} }
globalPolicySys.Set(bucketName, *bucketPolicy) globalPolicySys.Set(bucketName, bucketPolicy)
defer globalPolicySys.Remove(bucketName) defer globalPolicySys.Remove(bucketName)
// Unauthenticated ListObjects with READ bucket policy should succeed. // Unauthenticated ListObjects with READ bucket policy should succeed.
@ -906,7 +906,7 @@ func testUploadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler
if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil {
t.Fatalf("unexpected error. %v", err) t.Fatalf("unexpected error. %v", err)
} }
globalPolicySys.Set(bucketName, *bucketPolicy) globalPolicySys.Set(bucketName, bucketPolicy)
defer globalPolicySys.Remove(bucketName) defer globalPolicySys.Remove(bucketName)
// Unauthenticated upload with WRITE policy should succeed. // Unauthenticated upload with WRITE policy should succeed.
@ -1025,7 +1025,7 @@ func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandl
if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil {
t.Fatalf("unexpected error. %v", err) t.Fatalf("unexpected error. %v", err)
} }
globalPolicySys.Set(bucketName, *bucketPolicy) globalPolicySys.Set(bucketName, bucketPolicy)
defer globalPolicySys.Remove(bucketName) defer globalPolicySys.Remove(bucketName)
// Unauthenticated download with READ policy should succeed. // Unauthenticated download with READ policy should succeed.

View File

@ -161,18 +161,18 @@ func (r Retention) Retain(created time.Time) bool {
// BucketObjectLockConfig - map of bucket and retention configuration. // BucketObjectLockConfig - map of bucket and retention configuration.
type BucketObjectLockConfig struct { type BucketObjectLockConfig struct {
sync.RWMutex sync.RWMutex
retentionMap map[string]Retention retentionMap map[string]*Retention
} }
// Set - set retention configuration. // Set - set retention configuration.
func (config *BucketObjectLockConfig) Set(bucketName string, retention Retention) { func (config *BucketObjectLockConfig) Set(bucketName string, retention *Retention) {
config.Lock() config.Lock()
config.retentionMap[bucketName] = retention config.retentionMap[bucketName] = retention
config.Unlock() config.Unlock()
} }
// Get - Get retention configuration. // Get - Get retention configuration.
func (config *BucketObjectLockConfig) Get(bucketName string) (r Retention, ok bool) { func (config *BucketObjectLockConfig) Get(bucketName string) (r *Retention, ok bool) {
config.RLock() config.RLock()
defer config.RUnlock() defer config.RUnlock()
r, ok = config.retentionMap[bucketName] r, ok = config.retentionMap[bucketName]
@ -189,7 +189,7 @@ func (config *BucketObjectLockConfig) Remove(bucketName string) {
// NewBucketObjectLockConfig returns initialized BucketObjectLockConfig // NewBucketObjectLockConfig returns initialized BucketObjectLockConfig
func NewBucketObjectLockConfig() *BucketObjectLockConfig { func NewBucketObjectLockConfig() *BucketObjectLockConfig {
return &BucketObjectLockConfig{ return &BucketObjectLockConfig{
retentionMap: map[string]Retention{}, retentionMap: make(map[string]*Retention),
} }
} }
@ -280,7 +280,8 @@ func (config *Config) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
} }
// ToRetention - convert to Retention type. // ToRetention - convert to Retention type.
func (config *Config) ToRetention() (r Retention) { func (config *Config) ToRetention() (r *Retention) {
r = &Retention{}
if config.Rule != nil { if config.Rule != nil {
r.Mode = config.Rule.DefaultRetention.Mode r.Mode = config.Rule.DefaultRetention.Mode