From 9b3b04ecec50bf24641b5f40c82f70f7dedf1310 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 4 May 2020 09:42:58 -0700 Subject: [PATCH] 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. --- cmd/bucket-encryption-handlers.go | 11 ++- cmd/bucket-encryption.go | 21 +++--- cmd/bucket-handlers.go | 9 ++- cmd/bucket-lifecycle-handler.go | 2 +- cmd/{lifecycle.go => bucket-lifecycle.go} | 23 +++--- cmd/{object-lock.go => bucket-object-lock.go} | 4 +- cmd/bucket-policy-handlers.go | 2 +- cmd/{policy.go => bucket-policy.go} | 16 ++-- cmd/{quota.go => bucket-quota.go} | 75 ++++++++++--------- cmd/globals.go | 2 +- cmd/notification.go | 4 +- cmd/peer-rest-client.go | 4 +- cmd/peer-rest-server.go | 17 +++-- cmd/policy_test.go | 18 ++--- cmd/server-main.go | 12 ++- cmd/test-utils_test.go | 2 +- cmd/web-handlers.go | 2 +- cmd/web-handlers_test.go | 6 +- pkg/bucket/object/lock/lock.go | 11 +-- 19 files changed, 132 insertions(+), 109 deletions(-) rename cmd/{lifecycle.go => bucket-lifecycle.go} (87%) rename cmd/{object-lock.go => bucket-object-lock.go} (99%) rename cmd/{policy.go => bucket-policy.go} (96%) rename cmd/{quota.go => bucket-quota.go} (90%) diff --git a/cmd/bucket-encryption-handlers.go b/cmd/bucket-encryption-handlers.go index e4f9ae488..da2348cff 100644 --- a/cmd/bucket-encryption-handlers.go +++ b/cmd/bucket-encryption-handlers.go @@ -18,6 +18,7 @@ package cmd import ( "encoding/xml" + "fmt" "io" "net/http" @@ -69,7 +70,12 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r // Parse bucket encryption xml encConfig, err := validateBucketSSEConfig(io.LimitReader(r.Body, maxBucketSSEConfigSize)) 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 } @@ -86,7 +92,7 @@ func (api objectAPIHandlers) PutBucketEncryptionHandler(w http.ResponseWriter, r } // 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 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 globalBucketSSEConfigSys.Remove(bucket) + // Update peer MinIO servers of the updated bucket encryption config globalNotificationSys.RemoveBucketSSEConfig(ctx, bucket) diff --git a/cmd/bucket-encryption.go b/cmd/bucket-encryption.go index 365087d4f..b6c77a6aa 100644 --- a/cmd/bucket-encryption.go +++ b/cmd/bucket-encryption.go @@ -31,13 +31,13 @@ import ( // BucketSSEConfigSys - in-memory cache of bucket encryption config type BucketSSEConfigSys struct { sync.RWMutex - bucketSSEConfigMap map[string]bucketsse.BucketSSEConfig + bucketSSEConfigMap map[string]*bucketsse.BucketSSEConfig } // NewBucketSSEConfigSys - Creates an empty in-memory bucket encryption configuration cache func NewBucketSSEConfigSys() *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) if err != nil { if _, ok := err.(BucketSSEConfigNotFound); 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 @@ -73,7 +74,7 @@ func (sys *BucketSSEConfigSys) Init(buckets []BucketInfo, objAPI ObjectLayer) er } // 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. if globalIsGateway { objAPI := newObjectLayerWithoutSafeModeFn() @@ -85,7 +86,7 @@ func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSECon if err != nil { return } - return *cfg, true + return cfg, true } sys.Lock() @@ -95,7 +96,7 @@ func (sys *BucketSSEConfigSys) Get(bucket string) (config bucketsse.BucketSSECon } // 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. if globalIsGateway { return @@ -146,8 +147,8 @@ func removeBucketSSEConfig(ctx context.Context, objAPI ObjectLayer, bucket strin // Path to bucket-encryption.xml for the given bucket. configFile := path.Join(bucketConfigPrefix, bucket, bucketSSEConfig) - if err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile); err != nil { - if _, ok := err.(ObjectNotFound); ok { + if err := deleteConfig(ctx, objAPI, configFile); err != nil { + if err == errConfigNotFound { return BucketSSEConfigNotFound{Bucket: bucket} } return err diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 74dd17b9f..2a3c12189 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -589,8 +589,9 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req return } if objectLockEnabled { - globalBucketObjectLockConfig.Set(bucket, objectlock.Retention{}) - globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, objectlock.Retention{}) + ret := &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) globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, retention) } else { - globalBucketObjectLockConfig.Remove(bucket) - globalNotificationSys.RemoveBucketObjectLockConfig(ctx, bucket) + globalBucketObjectLockConfig.Set(bucket, &objectlock.Retention{}) + globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, &objectlock.Retention{}) } // Write success response. diff --git a/cmd/bucket-lifecycle-handler.go b/cmd/bucket-lifecycle-handler.go index 10ea678ae..285968058 100644 --- a/cmd/bucket-lifecycle-handler.go +++ b/cmd/bucket-lifecycle-handler.go @@ -77,7 +77,7 @@ func (api objectAPIHandlers) PutBucketLifecycleHandler(w http.ResponseWriter, r return } - globalLifecycleSys.Set(bucket, *bucketLifecycle) + globalLifecycleSys.Set(bucket, bucketLifecycle) globalNotificationSys.SetBucketLifecycle(ctx, bucket, bucketLifecycle) // Success. diff --git a/cmd/lifecycle.go b/cmd/bucket-lifecycle.go similarity index 87% rename from cmd/lifecycle.go rename to cmd/bucket-lifecycle.go index 8e780c617..b1784184a 100644 --- a/cmd/lifecycle.go +++ b/cmd/bucket-lifecycle.go @@ -35,11 +35,11 @@ const ( // LifecycleSys - Bucket lifecycle subsystem. type LifecycleSys struct { sync.RWMutex - bucketLifecycleMap map[string]lifecycle.Lifecycle + bucketLifecycleMap map[string]*lifecycle.Lifecycle } // 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 { // no-op return @@ -52,7 +52,7 @@ func (sys *LifecycleSys) Set(bucketName string, lifecycle lifecycle.Lifecycle) { } // 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 { // When gateway is enabled, no cached value // 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 { return } - return *l, true + return l, true } sys.Lock() @@ -104,8 +104,8 @@ func removeLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName s // Construct path to lifecycle.xml for the given bucket. configFile := path.Join(bucketConfigPrefix, bucketName, bucketLifecycleConfig) - if err := objAPI.DeleteObject(ctx, minioMetaBucket, configFile); err != nil { - if _, ok := err.(ObjectNotFound); ok { + if err := deleteConfig(ctx, objAPI, configFile); err != nil { + if err == errConfigNotFound { return BucketLifecycleNotFound{Bucket: bucketName} } return err @@ -116,7 +116,7 @@ func removeLifecycleConfig(ctx context.Context, objAPI ObjectLayer, bucketName s // NewLifecycleSys - creates new lifecycle system. func NewLifecycleSys() *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. func (sys *LifecycleSys) load(buckets []BucketInfo, objAPI ObjectLayer) error { for _, bucket := range buckets { - config, err := objAPI.GetBucketLifecycle(GlobalContext, bucket.Name) + config, err := getLifecycleConfig(objAPI, bucket.Name) if err != nil { 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 diff --git a/cmd/object-lock.go b/cmd/bucket-object-lock.go similarity index 99% rename from cmd/object-lock.go rename to cmd/bucket-object-lock.go index 0e240089a..552bff319 100644 --- a/cmd/object-lock.go +++ b/cmd/bucket-object-lock.go @@ -398,7 +398,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error configData, err := readConfig(ctx, objAPI, configFile) if err != nil { if errors.Is(err, errConfigNotFound) { - globalBucketObjectLockConfig.Set(bucket.Name, objectlock.Retention{}) + globalBucketObjectLockConfig.Set(bucket.Name, &objectlock.Retention{}) continue } return err @@ -408,7 +408,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error if err != nil { return err } - retention := objectlock.Retention{} + retention := &objectlock.Retention{} if config.Rule != nil { retention = config.ToRetention() } diff --git a/cmd/bucket-policy-handlers.go b/cmd/bucket-policy-handlers.go index 49516b39c..245c16cf6 100644 --- a/cmd/bucket-policy-handlers.go +++ b/cmd/bucket-policy-handlers.go @@ -92,7 +92,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht return } - globalPolicySys.Set(bucket, *bucketPolicy) + globalPolicySys.Set(bucket, bucketPolicy) globalNotificationSys.SetBucketPolicy(ctx, bucket, bucketPolicy) // Success. diff --git a/cmd/policy.go b/cmd/bucket-policy.go similarity index 96% rename from cmd/policy.go rename to cmd/bucket-policy.go index be626a564..7745e64a6 100644 --- a/cmd/policy.go +++ b/cmd/bucket-policy.go @@ -39,11 +39,11 @@ import ( // PolicySys - policy subsystem. type PolicySys struct { 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. -func (sys *PolicySys) Set(bucketName string, policy policy.Policy) { +func (sys *PolicySys) Set(bucketName string, policy *policy.Policy) { if globalIsGateway { // Set policy is a non-op under gateway mode. return @@ -97,12 +97,12 @@ func (sys *PolicySys) IsAllowed(args policy.Args) bool { // Loads policies for all buckets into PolicySys. func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error { for _, bucket := range buckets { - config, err := objAPI.GetBucketPolicy(GlobalContext, bucket.Name) + config, err := getPolicyConfig(objAPI, bucket.Name) if err != nil { if _, ok := err.(BucketPolicyNotFound); ok { - sys.Remove(bucket.Name) + continue } - continue + return err } // This part is specifically written to handle migration // when the Version string is empty, this was allowed @@ -118,7 +118,7 @@ func (sys *PolicySys) load(buckets []BucketInfo, objAPI ObjectLayer) error { return err } } - sys.Set(bucket.Name, *config) + sys.Set(bucket.Name, config) } 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. func (sys *PolicySys) Init(buckets []BucketInfo, objAPI ObjectLayer) error { if objAPI == nil { - return errInvalidArgument + return errServerNotInitialized } // 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. func NewPolicySys() *PolicySys { return &PolicySys{ - bucketPolicyMap: make(map[string]policy.Policy), + bucketPolicyMap: make(map[string]*policy.Policy), } } diff --git a/cmd/quota.go b/cmd/bucket-quota.go similarity index 90% rename from cmd/quota.go rename to cmd/bucket-quota.go index 0beebf368..6f7103573 100644 --- a/cmd/quota.go +++ b/cmd/bucket-quota.go @@ -60,23 +60,50 @@ func (sys *BucketQuotaSys) Remove(bucketName string) { sys.Unlock() } -// Exists - bucketName has Quota config set -func (sys *BucketQuotaSys) Exists(bucketName string) bool { - sys.RLock() - _, ok := sys.quotaMap[bucketName] - sys.RUnlock() - return ok -} - -// Keys - list of buckets with quota configuration -func (sys *BucketQuotaSys) Keys() []string { +// Buckets - list of buckets with quota configuration +func (sys *BucketQuotaSys) Buckets() []string { sys.RLock() defer sys.RUnlock() - var keys []string + var buckets []string 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 @@ -131,26 +158,6 @@ func enforceBucketQuota(ctx context.Context, bucket string, size int64) error { 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 ( bgQuotaInterval = 1 * time.Hour ) @@ -182,7 +189,7 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) error { if env.Get(envDataUsageCrawlConf, config.EnableOn) == config.EnableOff { return nil } - for _, bucket := range globalBucketQuotaSys.Keys() { + for _, bucket := range globalBucketQuotaSys.Buckets() { // Check if the current bucket has quota restrictions, if not skip it cfg, ok := globalBucketQuotaSys.Get(bucket) if !ok { diff --git a/cmd/globals.go b/cmd/globals.go index 0bbf7164d..a2bb93743 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -218,7 +218,7 @@ var ( globalBucketObjectLockConfig = objectlock.NewBucketObjectLockConfig() - globalBucketQuotaSys = NewBucketQuotaSys() + globalBucketQuotaSys *BucketQuotaSys globalBucketStorageCache bucketStorageCache // Disk cache drives diff --git a/cmd/notification.go b/cmd/notification.go index 2eb726357..d1ed32a35 100644 --- a/cmd/notification.go +++ b/cmd/notification.go @@ -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. func (sys *NotificationSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error { if objAPI == nil { - return errInvalidArgument + return errServerNotInitialized } // 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. -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)) for index, client := range sys.peerClients { if client == nil { diff --git a/cmd/peer-rest-client.go b/cmd/peer-rest-client.go index bd0b105b1..d2a3c306f 100644 --- a/cmd/peer-rest-client.go +++ b/cmd/peer-rest-client.go @@ -656,12 +656,12 @@ func (client *peerRESTClient) PutBucketNotification(bucket string, rulesMap even } // 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.Set(peerRESTBucket, bucket) var reader bytes.Buffer - err := gob.NewEncoder(&reader).Encode(&retention) + err := gob.NewEncoder(&reader).Encode(retention) if err != nil { return err } diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 071915b3a..032999c92 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -675,13 +675,14 @@ func (s *peerRESTServer) SetBucketPolicyHandler(w http.ResponseWriter, r *http.R s.writeErrorResponse(w, errors.New("Bucket name is missing")) return } - var policyData policy.Policy + if r.ContentLength < 0 { s.writeErrorResponse(w, errInvalidArgument) return } - err := gob.NewDecoder(r.Body).Decode(&policyData) + var policyData = &policy.Policy{} + err := gob.NewDecoder(r.Body).Decode(policyData) if err != nil { s.writeErrorResponse(w, err) return @@ -716,13 +717,13 @@ func (s *peerRESTServer) SetBucketLifecycleHandler(w http.ResponseWriter, r *htt s.writeErrorResponse(w, errors.New("Bucket name is missing")) return } - var lifecycleData lifecycle.Lifecycle if r.ContentLength < 0 { s.writeErrorResponse(w, errInvalidArgument) return } - err := gob.NewDecoder(r.Body).Decode(&lifecycleData) + var lifecycleData = &lifecycle.Lifecycle{} + err := gob.NewDecoder(r.Body).Decode(lifecycleData) if err != nil { s.writeErrorResponse(w, err) return @@ -758,13 +759,13 @@ func (s *peerRESTServer) SetBucketSSEConfigHandler(w http.ResponseWriter, r *htt return } - var encConfig bucketsse.BucketSSEConfig if r.ContentLength < 0 { s.writeErrorResponse(w, errInvalidArgument) return } - err := gob.NewDecoder(r.Body).Decode(&encConfig) + var encConfig = &bucketsse.BucketSSEConfig{} + err := gob.NewDecoder(r.Body).Decode(encConfig) if err != nil { s.writeErrorResponse(w, err) return @@ -860,13 +861,13 @@ func (s *peerRESTServer) PutBucketObjectLockConfigHandler(w http.ResponseWriter, return } - var retention objectlock.Retention if r.ContentLength < 0 { s.writeErrorResponse(w, errInvalidArgument) return } - err := gob.NewDecoder(r.Body).Decode(&retention) + var retention = &objectlock.Retention{} + err := gob.NewDecoder(r.Body).Decode(retention) if err != nil { s.writeErrorResponse(w, err) return diff --git a/cmd/policy_test.go b/cmd/policy_test.go index 487d6034a..378fd8741 100644 --- a/cmd/policy_test.go +++ b/cmd/policy_test.go @@ -41,10 +41,10 @@ func TestPolicySysSet(t *testing.T) { }, } case1Result := NewPolicySys() - case1Result.bucketPolicyMap["mybucket"] = case1Policy + case1Result.bucketPolicyMap["mybucket"] = &case1Policy case2PolicySys := NewPolicySys() - case2PolicySys.bucketPolicyMap["mybucket"] = case1Policy + case2PolicySys.bucketPolicyMap["mybucket"] = &case1Policy case2Policy := policy.Policy{ Version: policy.DefaultVersion, Statements: []policy.Statement{ @@ -58,10 +58,10 @@ func TestPolicySysSet(t *testing.T) { }, } case2Result := NewPolicySys() - case2Result.bucketPolicyMap["mybucket"] = case2Policy + case2Result.bucketPolicyMap["mybucket"] = &case2Policy case3PolicySys := NewPolicySys() - case3PolicySys.bucketPolicyMap["mybucket"] = case2Policy + case3PolicySys.bucketPolicyMap["mybucket"] = &case2Policy case3Policy := policy.Policy{ ID: "MyPolicyForMyBucket", Version: policy.DefaultVersion, @@ -81,7 +81,7 @@ func TestPolicySysSet(t *testing.T) { for i, testCase := range testCases { result := testCase.policySys - result.Set(testCase.bucketName, testCase.bucketPolicy) + result.Set(testCase.bucketName, &testCase.bucketPolicy) if !reflect.DeepEqual(result, testCase.expectedResult) { 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.bucketPolicyMap["mybucket"] = case1Policy + case1PolicySys.bucketPolicyMap["mybucket"] = &case1Policy case1Result := NewPolicySys() case2Policy := policy.Policy{ @@ -119,9 +119,9 @@ func TestPolicySysRemove(t *testing.T) { }, } case2PolicySys := NewPolicySys() - case2PolicySys.bucketPolicyMap["mybucket"] = case2Policy + case2PolicySys.bucketPolicyMap["mybucket"] = &case2Policy case2Result := NewPolicySys() - case2Result.bucketPolicyMap["mybucket"] = case2Policy + case2Result.bucketPolicyMap["mybucket"] = &case2Policy case3PolicySys := NewPolicySys() case3Result := NewPolicySys() @@ -148,7 +148,7 @@ func TestPolicySysRemove(t *testing.T) { func TestPolicySysIsAllowed(t *testing.T) { policySys := NewPolicySys() - policySys.Set("mybucket", policy.Policy{ + policySys.Set("mybucket", &policy.Policy{ Version: policy.DefaultVersion, Statements: []policy.Statement{ policy.NewStatement( diff --git a/cmd/server-main.go b/cmd/server-main.go index 058b52929..1e090ebc5 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -155,6 +155,9 @@ func newAllSubsystems() { // Create new bucket encryption subsystem globalBucketSSEConfigSys = NewBucketSSEConfigSys() + + // Create new bucket quota subsystem + globalBucketQuotaSys = NewBucketQuotaSys() } 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 { 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. 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) } + // 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 } diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index d62eb2808..57f616c70 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -1750,7 +1750,7 @@ func ExecObjectLayerAPIAnonTest(t *testing.T, obj ObjectLayer, testName, bucketN if err := obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { t.Fatalf("unexpected error. %v", err) } - globalPolicySys.Set(bucketName, *bucketPolicy) + globalPolicySys.Set(bucketName, bucketPolicy) defer globalPolicySys.Remove(bucketName) // now call the handler again with the unsigned/anonymous request, it should be accepted. diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 0d1453c8a..91a5511e7 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -1904,7 +1904,7 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic return toJSONError(ctx, err, args.BucketName) } - globalPolicySys.Set(args.BucketName, *bucketPolicy) + globalPolicySys.Set(args.BucketName, bucketPolicy) globalNotificationSys.SetBucketPolicy(ctx, args.BucketName, bucketPolicy) } diff --git a/cmd/web-handlers_test.go b/cmd/web-handlers_test.go index a0a167349..c98c8e2e2 100644 --- a/cmd/web-handlers_test.go +++ b/cmd/web-handlers_test.go @@ -548,7 +548,7 @@ func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHa if err = obj.SetBucketPolicy(context.Background(), bucketName, bucketPolicy); err != nil { t.Fatalf("unexpected error. %v", err) } - globalPolicySys.Set(bucketName, *bucketPolicy) + globalPolicySys.Set(bucketName, bucketPolicy) defer globalPolicySys.Remove(bucketName) // 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 { t.Fatalf("unexpected error. %v", err) } - globalPolicySys.Set(bucketName, *bucketPolicy) + globalPolicySys.Set(bucketName, bucketPolicy) defer globalPolicySys.Remove(bucketName) // 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 { t.Fatalf("unexpected error. %v", err) } - globalPolicySys.Set(bucketName, *bucketPolicy) + globalPolicySys.Set(bucketName, bucketPolicy) defer globalPolicySys.Remove(bucketName) // Unauthenticated download with READ policy should succeed. diff --git a/pkg/bucket/object/lock/lock.go b/pkg/bucket/object/lock/lock.go index 372680105..fafd4acf3 100644 --- a/pkg/bucket/object/lock/lock.go +++ b/pkg/bucket/object/lock/lock.go @@ -161,18 +161,18 @@ func (r Retention) Retain(created time.Time) bool { // BucketObjectLockConfig - map of bucket and retention configuration. type BucketObjectLockConfig struct { sync.RWMutex - retentionMap map[string]Retention + retentionMap map[string]*Retention } // Set - set retention configuration. -func (config *BucketObjectLockConfig) Set(bucketName string, retention Retention) { +func (config *BucketObjectLockConfig) Set(bucketName string, retention *Retention) { config.Lock() config.retentionMap[bucketName] = retention config.Unlock() } // 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() defer config.RUnlock() r, ok = config.retentionMap[bucketName] @@ -189,7 +189,7 @@ func (config *BucketObjectLockConfig) Remove(bucketName string) { // NewBucketObjectLockConfig returns initialized BucketObjectLockConfig func NewBucketObjectLockConfig() *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. -func (config *Config) ToRetention() (r Retention) { +func (config *Config) ToRetention() (r *Retention) { + r = &Retention{} if config.Rule != nil { r.Mode = config.Rule.DefaultRetention.Mode