mirror of
https://github.com/minio/minio.git
synced 2025-04-22 19:35:47 -04:00
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:
parent
3e063cca5c
commit
9b3b04ecec
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
@ -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()
|
||||||
}
|
}
|
@ -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.
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user