cleanup object-lock/bucket tagging for gateways (#9548)

This PR is to ensure that we call the relevant object
layer APIs for necessary S3 API level functionalities
allowing gateway implementations to return proper
errors as NotImplemented{}

This allows for all our tests in mint to behave
appropriately and can be handled appropriately as
well.
This commit is contained in:
Harshavardhana 2020-05-08 13:44:44 -07:00 committed by GitHub
parent 6885c72f32
commit a1de9cec58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 681 additions and 375 deletions

View File

@ -1808,6 +1808,10 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
apiErr = ErrNoSuchLifecycleConfiguration
case BucketSSEConfigNotFound:
apiErr = ErrNoSuchBucketSSEConfig
case BucketTaggingNotFound:
apiErr = ErrBucketTaggingNotFound
case BucketObjectLockConfigNotFound:
apiErr = ErrObjectLockConfigurationNotFound
case BucketQuotaConfigNotFound:
apiErr = ErrAdminNoSuchQuotaConfiguration
case BucketQuotaExceeded:

View File

@ -35,7 +35,7 @@ func runPutObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
// obtains random bucket name.
bucket := getRandomBucketName()
// create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucket, "")
err = obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}
@ -76,7 +76,7 @@ func runPutObjectPartBenchmark(b *testing.B, obj ObjectLayer, partSize int) {
object := getRandomObjectName()
// create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucket, "")
err = obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}
@ -181,7 +181,7 @@ func runGetObjectBenchmark(b *testing.B, obj ObjectLayer, objSize int) {
// obtains random bucket name.
bucket := getRandomBucketName()
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}
@ -278,7 +278,7 @@ func runPutObjectBenchmarkParallel(b *testing.B, obj ObjectLayer, objSize int) {
// obtains random bucket name.
bucket := getRandomBucketName()
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}
@ -322,7 +322,7 @@ func runGetObjectBenchmarkParallel(b *testing.B, obj ObjectLayer, objSize int) {
// obtains random bucket name.
bucket := getRandomBucketName()
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}

View File

@ -292,16 +292,6 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
for i := range bucketsInfo {
meta, err := loadBucketMetadata(ctx, objectAPI, bucketsInfo[i].Name)
if err == nil {
bucketsInfo[i].Created = meta.Created
}
if err != errMetaDataConverted {
logger.LogIf(ctx, err)
}
}
}
if s3Error == ErrAccessDenied {
@ -409,7 +399,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
continue
}
if _, ok := globalBucketObjectLockConfig.Get(bucket); ok {
if _, ok := globalBucketObjectLockSys.Get(bucket); ok {
if err := enforceRetentionBypassForDelete(ctx, r, bucket, object.ObjectName, getObjectInfoFn); err != ErrNone {
dErrs[index] = err
continue
@ -537,14 +527,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
if err != nil {
if err == dns.ErrNoEntriesFound {
// Proceed to creating a bucket.
if err = objectAPI.MakeBucketWithLocation(ctx, bucket, location); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
meta := newBucketMetadata(bucket)
meta.LockEnabled = objectLockEnabled
if err := meta.save(ctx, objectAPI); err != nil {
if err = objectAPI.MakeBucketWithLocation(ctx, bucket, location, objectLockEnabled); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
@ -579,25 +562,17 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
}
// Proceed to creating a bucket.
err := objectAPI.MakeBucketWithLocation(ctx, bucket, location)
err := objectAPI.MakeBucketWithLocation(ctx, bucket, location, objectLockEnabled)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if !globalIsGateway {
meta := newBucketMetadata(bucket)
meta.LockEnabled = objectLockEnabled
if err := meta.save(ctx, objectAPI); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if objectLockEnabled {
if objectLockEnabled && !globalIsGateway {
ret := &objectlock.Retention{}
globalBucketObjectLockConfig.Set(bucket, ret)
globalBucketObjectLockSys.Set(bucket, ret)
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, ret)
}
}
// Make sure to add Location information here only for bucket
w.Header().Set(xhttp.Location, path.Clean(r.URL.Path)) // Clean any trailing slashes.
@ -933,7 +908,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
}
}
if _, ok := globalBucketObjectLockConfig.Get(bucket); ok && forceDelete {
if _, ok := globalBucketObjectLockSys.Get(bucket); ok && forceDelete {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL, guessIsBrowserReq(r))
return
}
@ -949,15 +924,12 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
if globalDNSConfig != nil {
if err := globalDNSConfig.Delete(bucket); err != nil {
// Deleting DNS entry failed, attempt to create the bucket again.
objectAPI.MakeBucketWithLocation(ctx, bucket, "")
objectAPI.MakeBucketWithLocation(ctx, bucket, "", false)
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
}
// Delete metadata, only log errors.
logger.LogIf(ctx, newBucketMetadata(bucket).delete(ctx, objectAPI))
globalNotificationSys.DeleteBucket(ctx, bucket)
// Write success response.
@ -1041,6 +1013,7 @@ func (api objectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWri
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return
}
config, err := objectlock.ParseObjectLockConfig(r.Body)
if err != nil {
apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
@ -1049,32 +1022,17 @@ func (api objectAPIHandlers) PutBucketObjectLockConfigHandler(w http.ResponseWri
return
}
meta, err := loadBucketMetadata(ctx, objectAPI, bucket)
if err != nil {
if err = objectAPI.SetBucketObjectLockConfig(ctx, bucket, config); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if !meta.LockEnabled {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrObjectLockConfigurationNotAllowed), r.URL, guessIsBrowserReq(r))
return
}
data, err := xml.Marshal(config)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
if err = saveConfig(ctx, objectAPI, configFile, data); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if config.Rule != nil {
retention := config.ToRetention()
globalBucketObjectLockConfig.Set(bucket, retention)
globalBucketObjectLockSys.Set(bucket, retention)
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, retention)
} else {
globalBucketObjectLockConfig.Set(bucket, &objectlock.Retention{})
globalBucketObjectLockSys.Set(bucket, &objectlock.Retention{})
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, &objectlock.Retention{})
}
@ -1106,29 +1064,17 @@ func (api objectAPIHandlers) GetBucketObjectLockConfigHandler(w http.ResponseWri
return
}
meta, err := loadBucketMetadata(ctx, objectAPI, bucket)
if err != nil && err != errMetaDataConverted {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if !meta.LockEnabled {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrObjectLockConfigurationNotAllowed), r.URL, guessIsBrowserReq(r))
return
}
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
configData, err := readConfig(ctx, objectAPI, configFile)
lkCfg, err := objectAPI.GetBucketObjectLockConfig(ctx, bucket)
if err != nil {
if err != errConfigNotFound {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
if configData, err = xml.Marshal(objectlock.NewObjectLockConfig()); err != nil {
configData, err := xml.Marshal(lkCfg)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
}
// Write success response.
writeSuccessResponseXML(w, configData)
@ -1154,6 +1100,7 @@ func (api objectAPIHandlers) PutBucketTaggingHandler(w http.ResponseWriter, r *h
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return
}
tags, err := tags.ParseBucketXML(io.LimitReader(r.Body, r.ContentLength))
if err != nil {
apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
@ -1161,13 +1108,8 @@ func (api objectAPIHandlers) PutBucketTaggingHandler(w http.ResponseWriter, r *h
writeErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
return
}
data, err := xml.Marshal(tags)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
if err = saveConfig(ctx, objectAPI, configFile, data); err != nil {
if err = objectAPI.SetBucketTagging(ctx, bucket, tags); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
@ -1191,21 +1133,22 @@ func (api objectAPIHandlers) GetBucketTaggingHandler(w http.ResponseWriter, r *h
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
return
}
// check if user has permissions to perform this operation
if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketTaggingAction, bucket, ""); s3Error != ErrNone {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
return
}
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
configData, err := readConfig(ctx, objectAPI, configFile)
t, err := objectAPI.GetBucketTagging(ctx, bucket)
if err != nil {
var aerr APIError
if err == errConfigNotFound {
aerr = errorCodes.ToAPIErr(ErrBucketTaggingNotFound)
} else {
aerr = toAPIError(ctx, err)
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
writeErrorResponse(ctx, w, aerr, r.URL, guessIsBrowserReq(r))
configData, err := xml.Marshal(t)
if err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}
@ -1234,8 +1177,7 @@ func (api objectAPIHandlers) DeleteBucketTaggingHandler(w http.ResponseWriter, r
return
}
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
if err := deleteConfig(ctx, objectAPI, configFile); err != nil && err != errConfigNotFound {
if err := objectAPI.DeleteBucketTagging(ctx, bucket); err != nil {
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
return
}

View File

@ -154,14 +154,12 @@ func (b *bucketMetadata) save(ctx context.Context, o ObjectLayer) error {
return saveConfig(ctx, o, configFile, data)
}
// delete the config metadata.
// removeBucketMeta bucket metadata.
// If config does not exist no error is returned.
func (b bucketMetadata) delete(ctx context.Context, o ObjectLayer) error {
configFile := path.Join(bucketConfigPrefix, b.Name, bucketMetadataFile)
err := deleteConfig(ctx, o, configFile)
if err == errConfigNotFound {
// We don't care
err = nil
}
func removeBucketMeta(ctx context.Context, obj ObjectLayer, bucket string) error {
configFile := path.Join(bucketConfigPrefix, bucket, bucketMetadataFile)
if err := deleteConfig(ctx, obj, configFile); err != nil && err != errConfigNotFound {
return err
}
return nil
}

View File

@ -19,10 +19,12 @@ package cmd
import (
"bytes"
"context"
"encoding/xml"
"errors"
"math"
"net/http"
"path"
"sync"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/auth"
@ -30,6 +32,54 @@ import (
"github.com/minio/minio/pkg/bucket/policy"
)
// BucketObjectLockSys - map of bucket and retention configuration.
type BucketObjectLockSys struct {
sync.RWMutex
retentionMap map[string]*objectlock.Retention
}
// Set - set retention configuration.
func (sys *BucketObjectLockSys) Set(bucketName string, retention *objectlock.Retention) {
if globalIsGateway {
// no-op
return
}
sys.Lock()
sys.retentionMap[bucketName] = retention
sys.Unlock()
}
// Get - Get retention configuration.
func (sys *BucketObjectLockSys) Get(bucketName string) (r *objectlock.Retention, ok bool) {
if globalIsGateway {
// When gateway is enabled, no cached value
// is used to validate bucket object lock configuration
objAPI := newObjectLayerWithoutSafeModeFn()
if objAPI == nil {
return
}
lc, err := objAPI.GetBucketObjectLockConfig(GlobalContext, bucketName)
if err != nil {
return
}
return lc.ToRetention(), true
}
sys.RLock()
defer sys.RUnlock()
r, ok = sys.retentionMap[bucketName]
return r, ok
}
// Remove - removes retention sysuration.
func (sys *BucketObjectLockSys) Remove(bucketName string) {
sys.Lock()
delete(sys.retentionMap, bucketName)
sys.Unlock()
}
// Similar to enforceRetentionBypassForDelete but for WebUI
func enforceRetentionBypassForDeleteWeb(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, govBypassPerms bool) APIErrorCode {
opts, err := getOpts(ctx, r, bucket, object)
@ -295,7 +345,7 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
retentionRequested := objectlock.IsObjectLockRetentionRequested(r.Header)
legalHoldRequested := objectlock.IsObjectLockLegalHoldRequested(r.Header)
retentionCfg, isWORMBucket := globalBucketObjectLockConfig.Get(bucket)
retentionCfg, isWORMBucket := globalBucketObjectLockSys.Get(bucket)
if !isWORMBucket {
if legalHoldRequested || retentionRequested {
return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration
@ -378,7 +428,74 @@ func checkPutObjectLockAllowed(ctx context.Context, r *http.Request, bucket, obj
return mode, retainDate, legalHold, ErrNone
}
func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error {
func readBucketObjectLockConfig(ctx context.Context, objAPI ObjectLayer, bucket string) (*objectlock.Config, error) {
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
if err != nil && err != errMetaDataConverted {
return nil, toObjectErr(err, bucket)
}
if !meta.LockEnabled {
return nil, BucketObjectLockConfigNotFound{Bucket: bucket}
}
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
configData, err := readConfig(ctx, objAPI, configFile)
if err != nil {
if err != errConfigNotFound {
return nil, toObjectErr(err, bucket)
}
return objectlock.NewObjectLockConfig(), nil
}
cfg, err := objectlock.ParseObjectLockConfig(bytes.NewReader(configData))
if err != nil {
return nil, toObjectErr(err, bucket)
}
return cfg, nil
}
func saveBucketObjectLockConfig(ctx context.Context, objAPI ObjectLayer, bucket string, config *objectlock.Config) error {
meta, err := loadBucketMetadata(ctx, objAPI, bucket)
if err != nil && err != errMetaDataConverted {
return toObjectErr(err, bucket)
}
if !meta.LockEnabled {
return BucketObjectLockConfigNotFound{Bucket: bucket}
}
data, err := xml.Marshal(config)
if err != nil {
return toObjectErr(err, bucket)
}
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
if err = saveConfig(ctx, objAPI, configFile, data); err != nil {
return toObjectErr(err, bucket)
}
return nil
}
// NewBucketObjectLockSys returns initialized BucketObjectLockSys
func NewBucketObjectLockSys() *BucketObjectLockSys {
return &BucketObjectLockSys{
retentionMap: make(map[string]*objectlock.Retention),
}
}
// Init - initializes bucket object lock config system for all buckets
func (sys *BucketObjectLockSys) Init(buckets []BucketInfo, objAPI ObjectLayer) error {
if objAPI == nil {
return errServerNotInitialized
}
// In gateway mode, we always fetch the bucket object lock configuration from the gateway backend.
// So, this is a no-op for gateway servers.
if globalIsGateway {
return nil
}
// Load BucketObjectLockSys once during boot.
return sys.load(buckets, objAPI)
}
func (sys *BucketObjectLockSys) load(buckets []BucketInfo, objAPI ObjectLayer) error {
for _, bucket := range buckets {
ctx := logger.SetReqInfo(GlobalContext, &logger.ReqInfo{BucketName: bucket.Name})
meta, err := loadBucketMetadata(ctx, objAPI, bucket.Name)
@ -398,7 +515,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{})
globalBucketObjectLockSys.Set(bucket.Name, &objectlock.Retention{})
continue
}
return err
@ -412,7 +529,7 @@ func initBucketObjectLockConfig(buckets []BucketInfo, objAPI ObjectLayer) error
if config.Rule != nil {
retention = config.ToRetention()
}
globalBucketObjectLockConfig.Set(bucket.Name, retention)
globalBucketObjectLockSys.Set(bucket.Name, retention)
}
return nil
}

View File

@ -102,7 +102,7 @@ func testPutBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string
credentials auth.Credentials, t *testing.T) {
bucketName1 := fmt.Sprintf("%s-1", bucketName)
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName1, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName1, "", false); err != nil {
t.Fatal(err)
}

View File

@ -198,7 +198,7 @@ func enforceFIFOQuota(ctx context.Context, objectAPI ObjectLayer) error {
if cfg.Type != madmin.FIFOQuota {
continue
}
_, bucketHasLockConfig := globalBucketObjectLockConfig.Get(bucket)
_, bucketHasLockConfig := globalBucketObjectLockSys.Get(bucket)
dataUsageInfo, err := loadDataUsageFromBackend(ctx, objectAPI)
if err != nil {

61
cmd/bucket-tagging.go Normal file
View File

@ -0,0 +1,61 @@
/*
* MinIO Cloud Storage, (C) 2020 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"context"
"encoding/xml"
"path"
"github.com/minio/minio-go/v6/pkg/tags"
)
func saveBucketTagging(ctx context.Context, objAPI ObjectLayer, bucket string, t *tags.Tags) error {
data, err := xml.Marshal(t)
if err != nil {
return toObjectErr(err, bucket)
}
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
if err := saveConfig(ctx, objAPI, configFile, data); err != nil {
return toObjectErr(err, bucket)
}
return nil
}
func deleteBucketTagging(ctx context.Context, objAPI ObjectLayer, bucket string) error {
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
if err := deleteConfig(ctx, objAPI, configFile); err != nil && err != errConfigNotFound {
return toObjectErr(err, bucket)
}
return nil
}
func readBucketTagging(ctx context.Context, objAPI ObjectLayer, bucket string) (*tags.Tags, error) {
configFile := path.Join(bucketConfigPrefix, bucket, bucketTaggingConfigFile)
configData, err := readConfig(ctx, objAPI, configFile)
if err != nil {
if err == errConfigNotFound {
return nil, BucketTaggingNotFound{Bucket: bucket}
}
return nil, toObjectErr(err, bucket)
}
t := &tags.Tags{}
if err = xml.Unmarshal(configData, t); err != nil {
return nil, toObjectErr(err, bucket)
}
return t, nil
}

View File

@ -63,7 +63,7 @@ func lifecycleRound(ctx context.Context, objAPI ObjectLayer) error {
continue
}
_, bucketHasLockConfig := globalBucketObjectLockConfig.Get(bucket.Name)
_, bucketHasLockConfig := globalBucketObjectLockSys.Get(bucket.Name)
// Calculate the common prefix of all lifecycle rules
var prefixes []string

View File

@ -53,7 +53,7 @@ func TestReadFSMetadata(t *testing.T) {
bucketName := "bucket"
objectName := "object"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Unexpected err: ", err)
}
if _, err := obj.PutObject(GlobalContext, bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), ObjectOptions{}); err != nil {
@ -88,7 +88,7 @@ func TestWriteFSMetadata(t *testing.T) {
bucketName := "bucket"
objectName := "object"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Unexpected err: ", err)
}
if _, err := obj.PutObject(GlobalContext, bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), ObjectOptions{}); err != nil {

View File

@ -40,7 +40,7 @@ func TestFSCleanupMultipartUploadsInRoutine(t *testing.T) {
// Create a context we can cancel.
ctx, cancel := context.WithCancel(GlobalContext)
obj.MakeBucketWithLocation(ctx, bucketName, "")
obj.MakeBucketWithLocation(ctx, bucketName, "", false)
uploadID, err := obj.NewMultipartUpload(ctx, bucketName, objectName, ObjectOptions{})
if err != nil {
@ -81,7 +81,7 @@ func TestNewMultipartUploadFaultyDisk(t *testing.T) {
bucketName := "bucket"
objectName := "object"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}
@ -106,7 +106,7 @@ func TestPutObjectPartFaultyDisk(t *testing.T) {
data := []byte("12345")
dataLen := int64(len(data))
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}
@ -139,7 +139,7 @@ func TestCompleteMultipartUploadFaultyDisk(t *testing.T) {
objectName := "object"
data := []byte("12345")
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}
@ -172,7 +172,7 @@ func TestCompleteMultipartUpload(t *testing.T) {
objectName := "object"
data := []byte("12345")
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}
@ -204,7 +204,7 @@ func TestAbortMultipartUpload(t *testing.T) {
objectName := "object"
data := []byte("12345")
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}
@ -235,7 +235,7 @@ func TestListMultipartUploadsFaultyDisk(t *testing.T) {
bucketName := "bucket"
objectName := "object"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Cannot create bucket, err: ", err)
}

View File

@ -41,6 +41,7 @@ import (
"github.com/minio/minio/cmd/logger"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/color"
"github.com/minio/minio/pkg/lock"
@ -314,12 +315,17 @@ func (fs *FSObjects) statBucketDir(ctx context.Context, bucket string) (os.FileI
// MakeBucketWithLocation - create a new bucket, returns if it
// already exists.
func (fs *FSObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (fs *FSObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if lockEnabled {
return NotImplemented{}
}
bucketLock := fs.NewNSLock(ctx, bucket, "")
if err := bucketLock.GetLock(globalObjectTimeout); err != nil {
return err
}
defer bucketLock.Unlock()
// Verify if bucket is valid.
if s3utils.CheckValidBucketNameStrict(bucket) != nil {
return BucketNameInvalid{Bucket: bucket}
@ -339,6 +345,12 @@ func (fs *FSObjects) MakeBucketWithLocation(ctx context.Context, bucket, locatio
return toObjectErr(err, bucket)
}
meta := newBucketMetadata(bucket)
meta.LockEnabled = false
if err := meta.save(ctx, fs); err != nil {
return toObjectErr(err, bucket)
}
return nil
}
@ -381,7 +393,7 @@ func (fs *FSObjects) ListBuckets(ctx context.Context) ([]BucketInfo, error) {
}()
var bucketInfos []BucketInfo
entries, err := readDir((fs.fsPath))
entries, err := readDir(fs.fsPath)
if err != nil {
logger.LogIf(ctx, errDiskNotFound)
return nil, toObjectErr(errDiskNotFound)
@ -402,10 +414,17 @@ func (fs *FSObjects) ListBuckets(ctx context.Context) ([]BucketInfo, error) {
// Ignore any errors returned here.
continue
}
var created = fi.ModTime()
meta, err := loadBucketMetadata(ctx, fs, fi.Name())
if err == nil {
created = meta.Created
}
if err != errMetaDataConverted {
logger.LogIf(ctx, err)
}
bucketInfos = append(bucketInfos, BucketInfo{
Name: fi.Name(),
// As os.Stat() doesnt carry CreatedTime, use ModTime() as CreatedTime.
Created: fi.ModTime(),
Created: created,
})
}
@ -644,12 +663,12 @@ func (fs *FSObjects) GetObject(ctx context.Context, bucket, object string, offse
}
// Lock the object before reading.
objectLock := fs.NewNSLock(ctx, bucket, object)
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
lk := fs.NewNSLock(ctx, bucket, object)
if err := lk.GetRLock(globalObjectTimeout); err != nil {
logger.LogIf(ctx, err)
return err
}
defer objectLock.RUnlock()
defer lk.RUnlock()
atomic.AddInt64(&fs.activeIOCount, 1)
defer func() {
@ -818,11 +837,11 @@ func (fs *FSObjects) getObjectInfo(ctx context.Context, bucket, object string) (
// getObjectInfoWithLock - reads object metadata and replies back ObjectInfo.
func (fs *FSObjects) getObjectInfoWithLock(ctx context.Context, bucket, object string) (oi ObjectInfo, e error) {
// Lock the object before reading.
objectLock := fs.NewNSLock(ctx, bucket, object)
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
lk := fs.NewNSLock(ctx, bucket, object)
if err := lk.GetRLock(globalObjectTimeout); err != nil {
return oi, err
}
defer objectLock.RUnlock()
defer lk.RUnlock()
if err := checkGetObjArgs(ctx, bucket, object); err != nil {
return oi, err
@ -849,14 +868,14 @@ func (fs *FSObjects) GetObjectInfo(ctx context.Context, bucket, object string, o
oi, err := fs.getObjectInfoWithLock(ctx, bucket, object)
if err == errCorruptedFormat || err == io.EOF {
objectLock := fs.NewNSLock(ctx, bucket, object)
if err = objectLock.GetLock(globalObjectTimeout); err != nil {
lk := fs.NewNSLock(ctx, bucket, object)
if err = lk.GetLock(globalObjectTimeout); err != nil {
return oi, toObjectErr(err, bucket, object)
}
fsMetaPath := pathJoin(fs.fsPath, minioMetaBucket, bucketMetaPrefix, bucket, object, fs.metaJSONFile)
err = fs.createFsJSON(object, fsMetaPath)
objectLock.Unlock()
lk.Unlock()
if err != nil {
return oi, toObjectErr(err, bucket, object)
}
@ -896,12 +915,12 @@ func (fs *FSObjects) PutObject(ctx context.Context, bucket string, object string
}
// Lock the object.
objectLock := fs.NewNSLock(ctx, bucket, object)
if err := objectLock.GetLock(globalObjectTimeout); err != nil {
lk := fs.NewNSLock(ctx, bucket, object)
if err := lk.GetLock(globalObjectTimeout); err != nil {
logger.LogIf(ctx, err)
return objInfo, err
}
defer objectLock.Unlock()
defer lk.Unlock()
defer ObjectPathUpdated(path.Join(bucket, object))
atomic.AddInt64(&fs.activeIOCount, 1)
@ -1051,11 +1070,11 @@ func (fs *FSObjects) DeleteObjects(ctx context.Context, bucket string, objects [
// and there are no rollbacks supported.
func (fs *FSObjects) DeleteObject(ctx context.Context, bucket, object string) error {
// Acquire a write lock before deleting the object.
objectLock := fs.NewNSLock(ctx, bucket, object)
if err := objectLock.GetLock(globalOperationTimeout); err != nil {
lk := fs.NewNSLock(ctx, bucket, object)
if err := lk.GetLock(globalOperationTimeout); err != nil {
return err
}
defer objectLock.Unlock()
defer lk.Unlock()
if err := checkDelObjArgs(ctx, bucket, object); err != nil {
return err
@ -1364,6 +1383,31 @@ func (fs *FSObjects) DeleteBucketSSEConfig(ctx context.Context, bucket string) e
return removeBucketSSEConfig(ctx, fs, bucket)
}
// SetBucketObjectLockConfig enables/clears default object lock configuration
func (fs *FSObjects) SetBucketObjectLockConfig(ctx context.Context, bucket string, config *objectlock.Config) error {
return saveBucketObjectLockConfig(ctx, fs, bucket, config)
}
// GetBucketObjectLockConfig - returns current defaults for object lock configuration
func (fs *FSObjects) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
return readBucketObjectLockConfig(ctx, fs, bucket)
}
// SetBucketTagging sets bucket tags on given bucket
func (fs *FSObjects) SetBucketTagging(ctx context.Context, bucket string, t *tags.Tags) error {
return saveBucketTagging(ctx, fs, bucket, t)
}
// GetBucketTagging get bucket tags set on given bucket
func (fs *FSObjects) GetBucketTagging(ctx context.Context, bucket string) (*tags.Tags, error) {
return readBucketTagging(ctx, fs, bucket)
}
// DeleteBucketTagging delete bucket tags set if any.
func (fs *FSObjects) DeleteBucketTagging(ctx context.Context, bucket string) error {
return deleteBucketTagging(ctx, fs, bucket)
}
// ListObjectsV2 lists all blobs in bucket filtered by prefix
func (fs *FSObjects) ListObjectsV2(ctx context.Context, bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result ListObjectsV2Info, err error) {
marker := continuationToken

View File

@ -36,7 +36,7 @@ func TestFSParentDirIsObject(t *testing.T) {
bucketName := "testbucket"
objectName := "object"
if err = obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal(err)
}
objectContent := "12345"
@ -124,7 +124,7 @@ func TestFSShutdown(t *testing.T) {
fs := obj.(*FSObjects)
objectContent := "12345"
obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
obj.PutObject(GlobalContext, bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte(objectContent)), int64(len(objectContent)), "", ""), ObjectOptions{})
return fs, disk
}
@ -155,12 +155,12 @@ func TestFSGetBucketInfo(t *testing.T) {
fs := obj.(*FSObjects)
bucketName := "bucket"
err := obj.MakeBucketWithLocation(GlobalContext, "a", "")
err := obj.MakeBucketWithLocation(GlobalContext, "a", "", false)
if !isSameType(err, BucketNameInvalid{}) {
t.Fatal("BucketNameInvalid error not returned")
}
err = obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
err = obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
if err != nil {
t.Fatal(err)
}
@ -199,7 +199,7 @@ func TestFSPutObject(t *testing.T) {
bucketName := "bucket"
objectName := "1/2/3/4/object"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal(err)
}
@ -267,7 +267,7 @@ func TestFSDeleteObject(t *testing.T) {
bucketName := "bucket"
objectName := "object"
obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
obj.PutObject(GlobalContext, bucketName, objectName, mustGetPutObjReader(t, bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), ObjectOptions{})
// Test with invalid bucket name
@ -311,7 +311,7 @@ func TestFSDeleteBucket(t *testing.T) {
fs := obj.(*FSObjects)
bucketName := "bucket"
err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
if err != nil {
t.Fatal("Unexpected error: ", err)
}
@ -330,7 +330,7 @@ func TestFSDeleteBucket(t *testing.T) {
t.Fatal("Unexpected error: ", err)
}
obj.MakeBucketWithLocation(GlobalContext, bucketName, "")
obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
// Delete bucket should get error disk not found.
os.RemoveAll(disk)
@ -351,7 +351,7 @@ func TestFSListBuckets(t *testing.T) {
fs := obj.(*FSObjects)
bucketName := "bucket"
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err := obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal("Unexpected error: ", err)
}

View File

@ -25,6 +25,7 @@ import (
"github.com/minio/minio-go/v6/pkg/tags"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/madmin"
@ -198,6 +199,35 @@ func (a GatewayUnsupported) GetMetrics(ctx context.Context) (*Metrics, error) {
return &Metrics{}, NotImplemented{}
}
// SetBucketTagging - not implemented
func (a GatewayUnsupported) SetBucketTagging(ctx context.Context, bucket string, t *tags.Tags) error {
logger.LogIf(ctx, NotImplemented{})
return NotImplemented{}
}
// GetBucketObjectLockConfig - not implemented
func (a GatewayUnsupported) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
logger.LogIf(ctx, NotImplemented{})
return nil, NotImplemented{}
}
// SetBucketObjectLockConfig - not implemented
func (a GatewayUnsupported) SetBucketObjectLockConfig(ctx context.Context, bucket string, _ *objectlock.Config) error {
logger.LogIf(ctx, NotImplemented{})
return NotImplemented{}
}
// GetBucketTagging - not implemented
func (a GatewayUnsupported) GetBucketTagging(ctx context.Context, bucket string) (*tags.Tags, error) {
return nil, NotImplemented{}
}
// DeleteBucketTagging - not implemented.
func (a GatewayUnsupported) DeleteBucketTagging(ctx context.Context, bucket string) error {
logger.LogIf(ctx, NotImplemented{})
return NotImplemented{}
}
// PutObjectTag - not implemented.
func (a GatewayUnsupported) PutObjectTag(ctx context.Context, bucket, object string, tags string) error {
logger.LogIf(ctx, NotImplemented{})

View File

@ -544,7 +544,11 @@ func (a *azureObjects) StorageInfo(ctx context.Context, _ bool) (si minio.Storag
}
// MakeBucketWithLocation - Create a new container on azure backend.
func (a *azureObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (a *azureObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if lockEnabled {
return minio.NotImplemented{}
}
// Verify if bucket (container-name) is valid.
// IsValidBucketName has same restrictions as container names mentioned
// in azure documentation, so we will simply use the same function here.

View File

@ -19,13 +19,14 @@ package azure
import (
"encoding/base64"
"fmt"
"github.com/dustin/go-humanize"
"net/http"
"os"
"reflect"
"strconv"
"testing"
"github.com/dustin/go-humanize"
"github.com/Azure/azure-storage-blob-go/azblob"
minio "github.com/minio/minio/cmd"
)

View File

@ -421,7 +421,11 @@ func (l *gcsGateway) StorageInfo(ctx context.Context, _ bool) (si minio.StorageI
}
// MakeBucketWithLocation - Create a new container on GCS backend.
func (l *gcsGateway) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (l *gcsGateway) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if lockEnabled {
return minio.NotImplemented{}
}
bkt := l.client.Bucket(bucket)
// we'll default to the us multi-region in case of us-east-1

View File

@ -281,7 +281,11 @@ func (n *hdfsObjects) DeleteBucket(ctx context.Context, bucket string, forceDele
return hdfsToObjectErr(ctx, n.clnt.Remove(minio.PathJoin(hdfsSeparator, bucket)), bucket)
}
func (n *hdfsObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (n *hdfsObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if lockEnabled {
return minio.NotImplemented{}
}
if !hdfsIsValidBucketName(bucket) {
return minio.BucketNameInvalid{Bucket: bucket}
}

View File

@ -22,6 +22,7 @@ import (
"github.com/minio/cli"
minio "github.com/minio/minio/cmd"
"github.com/minio/minio/pkg/auth"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
)
const (
@ -121,6 +122,16 @@ type nasObjects struct {
minio.ObjectLayer
}
// GetBucketObjectLockConfig - not implemented
func (n *nasObjects) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
return nil, minio.NotImplemented{}
}
// SetBucketObjectLockConfig - not implemented
func (n *nasObjects) SetBucketObjectLockConfig(ctx context.Context, bucket string, _ *objectlock.Config) error {
return minio.NotImplemented{}
}
// IsReady returns whether the layer is ready to take requests.
func (n *nasObjects) IsReady(ctx context.Context) bool {
sinfo := n.ObjectLayer.StorageInfo(ctx, false)

View File

@ -281,7 +281,11 @@ func (l *s3Objects) StorageInfo(ctx context.Context, _ bool) (si minio.StorageIn
}
// MakeBucket creates a new container on S3 backend.
func (l *s3Objects) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (l *s3Objects) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if lockEnabled {
return minio.NotImplemented{}
}
// Verify if bucket name is valid.
// We are using a separate helper function here to validate bucket
// names instead of IsValidBucketName() because there is a possibility

View File

@ -35,7 +35,6 @@ import (
"github.com/minio/minio/cmd/crypto"
xhttp "github.com/minio/minio/cmd/http"
"github.com/minio/minio/pkg/auth"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/certs"
"github.com/minio/minio/pkg/event"
@ -216,8 +215,7 @@ var (
globalOperationTimeout = newDynamicTimeout(10*time.Minute /*30*/, 600*time.Second) // default timeout for general ops
globalHealingTimeout = newDynamicTimeout(30*time.Minute /*1*/, 30*time.Minute) // timeout for healing related ops
globalBucketObjectLockConfig = objectlock.NewBucketObjectLockConfig()
globalBucketObjectLockSys *BucketObjectLockSys
globalBucketQuotaSys *BucketQuotaSys
globalBucketStorageCache bucketStorageCache

View File

@ -566,7 +566,7 @@ func (sys *NotificationSys) SetBucketPolicy(ctx context.Context, bucketName stri
// DeleteBucket - calls DeleteBucket RPC call on all peers.
func (sys *NotificationSys) DeleteBucket(ctx context.Context, bucketName string) {
globalNotificationSys.RemoveNotification(bucketName)
globalBucketObjectLockConfig.Remove(bucketName)
globalBucketObjectLockSys.Remove(bucketName)
globalBucketQuotaSys.Remove(bucketName)
globalPolicySys.Remove(bucketName)
globalLifecycleSys.Remove(bucketName)

View File

@ -85,7 +85,10 @@ func deleteBucketMetadata(ctx context.Context, bucket string, objAPI ObjectLayer
removePolicyConfig(ctx, objAPI, bucket)
// Delete notification config, if present - ignore any errors.
removeNotificationConfig(ctx, objAPI, bucket)
logger.LogIf(ctx, removeNotificationConfig(ctx, objAPI, bucket))
// Delete bucket meta config, if present - ignore any errors.
logger.LogIf(ctx, removeBucketMeta(ctx, objAPI, bucket))
}
// Depending on the disk type network or local, initialize storage API.
@ -144,13 +147,11 @@ func cleanupDir(ctx context.Context, storage StorageAPI, volume, dirPath string)
// Removes notification.xml for a given bucket, only used during DeleteBucket.
func removeNotificationConfig(ctx context.Context, objAPI ObjectLayer, bucket string) error {
// Verify bucket is valid.
if !IsValidBucketName(bucket) {
return BucketNameInvalid{Bucket: bucket}
}
ncPath := path.Join(bucketConfigPrefix, bucket, bucketNotificationConfig)
return objAPI.DeleteObject(ctx, minioMetaBucket, ncPath)
if err := deleteConfig(ctx, objAPI, ncPath); err != nil && err != errConfigNotFound {
return err
}
return nil
}
func listObjectsNonSlash(ctx context.Context, bucket, prefix, marker, delimiter string, maxKeys int, tpool *TreeWalkPool, listDir ListDirFunc, getObjInfo func(context.Context, string, string) (ObjectInfo, error), getObjectInfoDirs ...func(context.Context, string, string) (ObjectInfo, error)) (loi ListObjectsInfo, err error) {

View File

@ -85,7 +85,7 @@ func testDeleteObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
for i, testCase := range testCases {
err := obj.MakeBucketWithLocation(context.Background(), testCase.bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), testCase.bucketName, "", false)
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())
}

View File

@ -255,21 +255,35 @@ func (e InvalidMarkerPrefixCombination) Error() string {
type BucketPolicyNotFound GenericError
func (e BucketPolicyNotFound) Error() string {
return "No bucket policy found for bucket: " + e.Bucket
return "No bucket policy configuration found for bucket: " + e.Bucket
}
// BucketLifecycleNotFound - no bucket lifecycle found.
type BucketLifecycleNotFound GenericError
func (e BucketLifecycleNotFound) Error() string {
return "No bucket life cycle found for bucket : " + e.Bucket
return "No bucket lifecycle configuration found for bucket : " + e.Bucket
}
// BucketSSEConfigNotFound - no bucket encryption config found
// BucketSSEConfigNotFound - no bucket encryption found
type BucketSSEConfigNotFound GenericError
func (e BucketSSEConfigNotFound) Error() string {
return "No bucket encryption found for bucket: " + e.Bucket
return "No bucket encryption configuration found for bucket: " + e.Bucket
}
// BucketTaggingNotFound - no bucket tags found
type BucketTaggingNotFound GenericError
func (e BucketTaggingNotFound) Error() string {
return "No bucket tags found for bucket: " + e.Bucket
}
// BucketObjectLockConfigNotFound - no bucket object lock config found
type BucketObjectLockConfigNotFound GenericError
func (e BucketObjectLockConfigNotFound) Error() string {
return "No bucket object lock configuration found for bucket: " + e.Bucket
}
// BucketQuotaConfigNotFound - no bucket quota config found.

View File

@ -42,7 +42,7 @@ func testGetObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
emptyDirName := "test-empty-dir/"
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
// Stop the test if creation of the bucket fails.
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())
@ -194,7 +194,7 @@ func testGetObjectPermissionDenied(obj ObjectLayer, instanceType string, disks [
// Setup for the tests.
bucketName := getRandomBucketName()
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
// Stop the test if creation of the bucket fails.
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())
@ -304,7 +304,7 @@ func testGetObjectDiskNotFound(obj ObjectLayer, instanceType string, disks []str
bucketName := getRandomBucketName()
objectName := "test-object"
// create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
// Stop the test if creation of the bucket fails.
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())

View File

@ -30,7 +30,7 @@ func TestGetObjectInfo(t *testing.T) {
// Testing GetObjectInfo().
func testGetObjectInfo(obj ObjectLayer, instanceType string, t TestErrHandler) {
// This bucket is used for testing getObjectInfo operations.
err := obj.MakeBucketWithLocation(context.Background(), "test-getobjectinfo", "")
err := obj.MakeBucketWithLocation(context.Background(), "test-getobjectinfo", "", false)
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())
}

View File

@ -1,5 +1,5 @@
/*
* MinIO Cloud Storage, (C) 2016 MinIO, Inc.
* MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import (
"github.com/minio/minio-go/v6/pkg/tags"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/madmin"
@ -64,7 +65,7 @@ type ObjectLayer interface {
StorageInfo(ctx context.Context, local bool) StorageInfo // local queries only local disks
// Bucket operations.
MakeBucketWithLocation(ctx context.Context, bucket string, location string) error
MakeBucketWithLocation(ctx context.Context, bucket string, location string, lockEnabled bool) error
GetBucketInfo(ctx context.Context, bucket string) (bucketInfo BucketInfo, err error)
ListBuckets(ctx context.Context) (buckets []BucketInfo, err error)
DeleteBucket(ctx context.Context, bucket string, forceDelete bool) error
@ -130,6 +131,15 @@ type ObjectLayer interface {
GetBucketSSEConfig(context.Context, string) (*bucketsse.BucketSSEConfig, error)
DeleteBucketSSEConfig(context.Context, string) error
// Bucket locking configuration operations
SetBucketObjectLockConfig(context.Context, string, *objectlock.Config) error
GetBucketObjectLockConfig(context.Context, string) (*objectlock.Config, error)
// Bucket tagging operations
SetBucketTagging(context.Context, string, *tags.Tags) error
GetBucketTagging(context.Context, string) (*tags.Tags, error)
DeleteBucketTagging(context.Context, string) error
// Backend related metrics
GetMetrics(ctx context.Context) (*Metrics, error)

View File

@ -49,7 +49,7 @@ func testListObjects(obj ObjectLayer, instanceType string, t1 TestErrHandler) {
"test-bucket-single-object",
}
for _, bucket := range testBuckets {
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
t.Fatalf("%s : %s", instanceType, err.Error())
}
@ -669,7 +669,7 @@ func BenchmarkListObjects(b *testing.B) {
bucket := "ls-benchmark-bucket"
// Create a bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucket, "")
err = obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
b.Fatal(err)
}

View File

@ -55,7 +55,7 @@ func testObjectNewMultipartUpload(obj ObjectLayer, instanceType string, t TestEr
}
// Create bucket before intiating NewMultipartUpload.
err = obj.MakeBucketWithLocation(context.Background(), bucket, "")
err = obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -89,7 +89,7 @@ func testObjectAbortMultipartUpload(obj ObjectLayer, instanceType string, t Test
object := "minio-object"
opts := ObjectOptions{}
// Create bucket before intiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -135,7 +135,7 @@ func testObjectAPIIsUploadIDExists(obj ObjectLayer, instanceType string, t TestE
object := "minio-object"
// Create bucket before intiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -166,7 +166,7 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrH
object := "minio-object"
opts := ObjectOptions{}
// Create bucket before intiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -178,7 +178,7 @@ func testObjectAPIPutObjectPart(obj ObjectLayer, instanceType string, t TestErrH
t.Fatalf("%s : %s", instanceType, err.Error())
}
// Creating a dummy bucket for tests.
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "")
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -302,7 +302,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
// objectNames[0].
// uploadIds [0].
// Create bucket before initiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "")
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -320,7 +320,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
// objectNames[0].
// uploadIds [1-3].
// Bucket to test for mutiple upload Id's for a given object.
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[1], "")
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[1], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -341,7 +341,7 @@ func testListMultipartUploads(obj ObjectLayer, instanceType string, t TestErrHan
// bucketnames[2].
// objectNames[0-2].
// uploadIds [4-9].
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[2], "")
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[2], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -1166,7 +1166,7 @@ func testListObjectPartsDiskNotFound(obj ObjectLayer, instanceType string, disks
// objectNames[0].
// uploadIds [0].
// Create bucket before intiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "")
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -1411,7 +1411,7 @@ func testListObjectParts(obj ObjectLayer, instanceType string, t TestErrHandler)
// objectNames[0].
// uploadIds [0].
// Create bucket before intiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "")
err := obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -1657,7 +1657,7 @@ func testObjectCompleteMultipartUpload(obj ObjectLayer, instanceType string, t T
// objectNames[0].
// uploadIds [0].
// Create bucket before intiating NewMultipartUpload.
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "")
err = obj.MakeBucketWithLocation(context.Background(), bucketNames[0], "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)

View File

@ -46,14 +46,14 @@ func testObjectAPIPutObject(obj ObjectLayer, instanceType string, t TestErrHandl
object := "minio-object"
// Create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
}
// Creating a dummy bucket for tests.
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "")
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -206,14 +206,14 @@ func testObjectAPIPutObjectDiskNotFound(obj ObjectLayer, instanceType string, di
object := "minio-object"
// Create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
}
// Creating a dummy bucket for tests.
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "")
err = obj.MakeBucketWithLocation(context.Background(), "unused-bucket", "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -318,7 +318,7 @@ func testObjectAPIPutObjectStaleFiles(obj ObjectLayer, instanceType string, disk
object := "minio-object"
// Create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -352,7 +352,7 @@ func testObjectAPIMultipartPutObjectStaleFiles(obj ObjectLayer, instanceType str
object := "minio-object"
// Create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucket, "")
err := obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())

View File

@ -2593,7 +2593,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
}
apiErr := ErrNone
if _, ok := globalBucketObjectLockConfig.Get(bucket); ok {
if _, ok := globalBucketObjectLockSys.Get(bucket); ok {
apiErr = enforceRetentionBypassForDelete(ctx, r, bucket, object, getObjectInfo)
if apiErr != ErrNone && apiErr != ErrNoSuchKey {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(apiErr), r.URL, guessIsBrowserReq(r))
@ -2657,7 +2657,7 @@ func (api objectAPIHandlers) PutObjectLegalHoldHandler(w http.ResponseWriter, r
return
}
if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok {
if _, ok := globalBucketObjectLockSys.Get(bucket); !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r))
return
}
@ -2739,7 +2739,7 @@ func (api objectAPIHandlers) GetObjectLegalHoldHandler(w http.ResponseWriter, r
getObjectInfo = api.CacheAPI().GetObjectInfo
}
if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok {
if _, ok := globalBucketObjectLockSys.Get(bucket); !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r))
return
}
@ -2816,7 +2816,7 @@ func (api objectAPIHandlers) PutObjectRetentionHandler(w http.ResponseWriter, r
return
}
if _, ok := globalBucketObjectLockConfig.Get(bucket); !ok {
if _, ok := globalBucketObjectLockSys.Get(bucket); !ok {
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketObjectLockConfiguration), r.URL, guessIsBrowserReq(r))
return
}

View File

@ -77,7 +77,7 @@ func (s *ObjectLayerAPISuite) TestMakeBucket(t *testing.T) {
// Tests validate bucket creation.
func testMakeBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "bucket-unknown", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket-unknown", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -91,7 +91,7 @@ func (s *ObjectLayerAPISuite) TestMultipartObjectCreation(t *testing.T) {
// Tests validate creation of part files during Multipart operation.
func testMultipartObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) {
var opts ObjectOptions
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -135,7 +135,7 @@ func (s *ObjectLayerAPISuite) TestMultipartObjectAbort(t *testing.T) {
// Tests validate abortion of Multipart operation.
func testMultipartObjectAbort(obj ObjectLayer, instanceType string, t TestErrHandler) {
var opts ObjectOptions
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -181,7 +181,7 @@ func (s *ObjectLayerAPISuite) TestMultipleObjectCreation(t *testing.T) {
func testMultipleObjectCreation(obj ObjectLayer, instanceType string, t TestErrHandler) {
objects := make(map[string][]byte)
var opts ObjectOptions
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -236,7 +236,7 @@ func (s *ObjectLayerAPISuite) TestPaging(t *testing.T) {
// Tests validate creation of objects and the order of listing using various filters for ListObjects operation.
func testPaging(obj ObjectLayer, instanceType string, t TestErrHandler) {
obj.MakeBucketWithLocation(context.Background(), "bucket", "")
obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
result, err := obj.ListObjects(context.Background(), "bucket", "", "", "", 0)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
@ -440,7 +440,7 @@ func (s *ObjectLayerAPISuite) TestObjectOverwriteWorks(t *testing.T) {
// Tests validate overwriting of an existing object.
func testObjectOverwriteWorks(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -494,11 +494,11 @@ func (s *ObjectLayerAPISuite) TestBucketRecreateFails(t *testing.T) {
// Tests validate that recreation of the bucket fails.
func testBucketRecreateFails(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "string", "")
err := obj.MakeBucketWithLocation(context.Background(), "string", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
err = obj.MakeBucketWithLocation(context.Background(), "string", "")
err = obj.MakeBucketWithLocation(context.Background(), "string", "", false)
if err == nil {
t.Fatalf("%s: Expected error but found nil.", instanceType)
}
@ -519,7 +519,7 @@ func testPutObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
length := int64(len(content))
readerEOF := newTestReaderEOF(content)
readerNoEOF := newTestReaderNoEOF(content)
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -559,7 +559,7 @@ func (s *ObjectLayerAPISuite) TestPutObjectInSubdir(t *testing.T) {
// Tests validate PutObject with subdirectory prefix.
func testPutObjectInSubdir(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -601,7 +601,7 @@ func testListBuckets(obj ObjectLayer, instanceType string, t TestErrHandler) {
}
// add one and test exists.
err = obj.MakeBucketWithLocation(context.Background(), "bucket1", "")
err = obj.MakeBucketWithLocation(context.Background(), "bucket1", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -615,7 +615,7 @@ func testListBuckets(obj ObjectLayer, instanceType string, t TestErrHandler) {
}
// add two and test exists.
err = obj.MakeBucketWithLocation(context.Background(), "bucket2", "")
err = obj.MakeBucketWithLocation(context.Background(), "bucket2", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -629,7 +629,7 @@ func testListBuckets(obj ObjectLayer, instanceType string, t TestErrHandler) {
}
// add three and test exists + prefix.
err = obj.MakeBucketWithLocation(context.Background(), "bucket22", "")
err = obj.MakeBucketWithLocation(context.Background(), "bucket22", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -653,11 +653,11 @@ func testListBucketsOrder(obj ObjectLayer, instanceType string, t TestErrHandler
// if implementation contains a map, order of map keys will vary.
// this ensures they return in the same order each time.
// add one and test exists.
err := obj.MakeBucketWithLocation(context.Background(), "bucket1", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket1", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
err = obj.MakeBucketWithLocation(context.Background(), "bucket2", "")
err = obj.MakeBucketWithLocation(context.Background(), "bucket2", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -706,7 +706,7 @@ func (s *ObjectLayerAPISuite) TestNonExistantObjectInBucket(t *testing.T) {
// Tests validate that GetObject fails on a non-existent bucket as expected.
func testNonExistantObjectInBucket(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -734,7 +734,7 @@ func (s *ObjectLayerAPISuite) TestGetDirectoryReturnsObjectNotFound(t *testing.T
// Tests validate that GetObject on an existing directory fails as expected.
func testGetDirectoryReturnsObjectNotFound(obj ObjectLayer, instanceType string, t TestErrHandler) {
bucketName := "bucket"
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}
@ -776,7 +776,7 @@ func (s *ObjectLayerAPISuite) TestContentType(t *testing.T) {
// Test content-type.
func testContentType(obj ObjectLayer, instanceType string, t TestErrHandler) {
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "")
err := obj.MakeBucketWithLocation(context.Background(), "bucket", "", false)
if err != nil {
t.Fatalf("%s: <ERROR> %s", instanceType, err)
}

View File

@ -598,7 +598,7 @@ func (s *peerRESTServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Requ
globalNotificationSys.RemoveNotification(bucketName)
globalPolicySys.Remove(bucketName)
globalBucketObjectLockConfig.Remove(bucketName)
globalBucketObjectLockSys.Remove(bucketName)
globalBucketQuotaSys.Remove(bucketName)
globalLifecycleSys.Remove(bucketName)
@ -843,7 +843,7 @@ func (s *peerRESTServer) RemoveBucketObjectLockConfigHandler(w http.ResponseWrit
return
}
globalBucketObjectLockConfig.Remove(bucketName)
globalBucketObjectLockSys.Remove(bucketName)
w.(http.Flusher).Flush()
}
@ -873,7 +873,7 @@ func (s *peerRESTServer) PutBucketObjectLockConfigHandler(w http.ResponseWriter,
return
}
globalBucketObjectLockConfig.Set(bucketName, retention)
globalBucketObjectLockSys.Set(bucketName, retention)
w.(http.Flusher).Flush()
}

View File

@ -141,7 +141,7 @@ func testPostPolicyBucketHandler(obj ObjectLayer, instanceType string, t TestErr
// objectNames[0].
// uploadIds [0].
// Create bucket before initiating NewMultipartUpload.
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())
@ -451,7 +451,7 @@ func testPostPolicyBucketHandlerRedirect(obj ObjectLayer, instanceType string, t
curTime := UTCNow()
curTimePlus5Min := curTime.Add(time.Minute * 5)
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// Failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err.Error())

View File

@ -156,6 +156,9 @@ func newAllSubsystems() {
// Create new bucket encryption subsystem
globalBucketSSEConfigSys = NewBucketSSEConfigSys()
// Create new bucket object lock subsystem
globalBucketObjectLockSys = NewBucketObjectLockSys()
// Create new bucket quota subsystem
globalBucketQuotaSys = NewBucketQuotaSys()
}
@ -281,7 +284,7 @@ func initAllSubsystems(newObject ObjectLayer) (err error) {
wquorum := &InsufficientWriteQuorum{}
rquorum := &InsufficientReadQuorum{}
for _, bucket := range buckets {
if err = newObject.MakeBucketWithLocation(GlobalContext, bucket.Name, ""); err != nil {
if err = newObject.MakeBucketWithLocation(GlobalContext, bucket.Name, "", false); err != nil {
if errors.As(err, &wquorum) || errors.As(err, &rquorum) {
// Retrun the error upwards for the caller to retry.
return fmt.Errorf("Unable to heal bucket: %w", err)
@ -329,11 +332,6 @@ func initAllSubsystems(newObject ObjectLayer) (err error) {
return fmt.Errorf("Unable to initialize policy system: %w", err)
}
// Initialize bucket object lock.
if err = initBucketObjectLockConfig(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize object lock system: %w", err)
}
// Initialize lifecycle system.
if err = globalLifecycleSys.Init(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize lifecycle system: %w", err)
@ -344,6 +342,11 @@ func initAllSubsystems(newObject ObjectLayer) (err error) {
return fmt.Errorf("Unable to initialize bucket encryption subsystem: %w", err)
}
// Initialize bucket object lock.
if err = globalBucketObjectLockSys.Init(buckets, newObject); err != nil {
return fmt.Errorf("Unable to initialize object lock system: %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)

View File

@ -1640,7 +1640,7 @@ func initAPIHandlerTest(obj ObjectLayer, endpoints []string) (string, http.Handl
bucketName := getRandomBucketName()
// Create bucket.
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err := obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, return err.
return "", nil, err

View File

@ -172,7 +172,7 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
if _, err := globalDNSConfig.Get(args.BucketName); err != nil {
if err == dns.ErrNoEntriesFound {
// Proceed to creating a bucket.
if err = objectAPI.MakeBucketWithLocation(ctx, args.BucketName, globalServerRegion); err != nil {
if err = objectAPI.MakeBucketWithLocation(ctx, args.BucketName, globalServerRegion, false); err != nil {
return toJSONError(ctx, err)
}
if err = globalDNSConfig.Put(args.BucketName); err != nil {
@ -188,7 +188,7 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
return toJSONError(ctx, errBucketAlreadyExists)
}
if err := objectAPI.MakeBucketWithLocation(ctx, args.BucketName, globalServerRegion); err != nil {
if err := objectAPI.MakeBucketWithLocation(ctx, args.BucketName, globalServerRegion, false); err != nil {
return toJSONError(ctx, err, args.BucketName)
}
@ -261,7 +261,7 @@ func (web *webAPIHandlers) DeleteBucket(r *http.Request, args *RemoveBucketArgs,
if globalDNSConfig != nil {
if err := globalDNSConfig.Delete(args.BucketName); err != nil {
// Deleting DNS entry failed, attempt to create the bucket again.
objectAPI.MakeBucketWithLocation(ctx, args.BucketName, "")
objectAPI.MakeBucketWithLocation(ctx, args.BucketName, "", false)
return toJSONError(ctx, err)
}
}

View File

@ -320,7 +320,7 @@ func testDeleteBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrH
bucketName := getRandomBucketName()
var opts ObjectOptions
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
t.Fatalf("failed to create bucket: %s (%s)", err.Error(), instanceType)
}
@ -398,7 +398,7 @@ func testDeleteBucketWebHandler(obj ObjectLayer, instanceType string, t TestErrH
continue
}
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create new bucket, abort.
t.Fatalf("failed to create new bucket (%s): %s", instanceType, err.Error())
@ -426,7 +426,7 @@ func testListBucketsWebHandler(obj ObjectLayer, instanceType string, t TestErrHa
bucketName := getRandomBucketName()
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -477,7 +477,7 @@ func testListObjectsWebHandler(obj ObjectLayer, instanceType string, t TestErrHa
objectSize := 1 * humanize.KiByte
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -581,7 +581,7 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
objectSize := 1 * humanize.KiByte
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -858,7 +858,7 @@ func testUploadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler
return rec.Code
}
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -955,7 +955,7 @@ func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandl
}
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -1060,7 +1060,7 @@ func testWebHandlerDownloadZip(obj ObjectLayer, instanceType string, t TestErrHa
fileThree := "cccccccccccccc"
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucket, "")
err = obj.MakeBucketWithLocation(context.Background(), bucket, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -1147,7 +1147,7 @@ func testWebPresignedGetHandler(obj ObjectLayer, instanceType string, t TestErrH
objectSize := 1 * humanize.KiByte
// Create bucket.
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
@ -1248,7 +1248,7 @@ func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
rec := httptest.NewRecorder()
bucketName := getRandomBucketName()
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false); err != nil {
t.Fatal("Unexpected error: ", err)
}
@ -1323,7 +1323,7 @@ func testWebListAllBucketPoliciesHandler(obj ObjectLayer, instanceType string, t
rec := httptest.NewRecorder()
bucketName := getRandomBucketName()
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false); err != nil {
t.Fatal("Unexpected error: ", err)
}
@ -1428,7 +1428,7 @@ func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
// Create a bucket
bucketName := getRandomBucketName()
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false); err != nil {
t.Fatal("Unexpected error: ", err)
}
@ -1587,7 +1587,7 @@ func TestWebObjectLayerFaultyDisks(t *testing.T) {
}
bucketName := "mybucket"
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "")
err = obj.MakeBucketWithLocation(context.Background(), bucketName, "", false)
if err != nil {
t.Fatal("Cannot make bucket:", err)
}

View File

@ -34,6 +34,7 @@ import (
"github.com/minio/minio/pkg/bpool"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/dsync"
"github.com/minio/minio/pkg/madmin"
@ -509,14 +510,14 @@ func (s *xlSets) Shutdown(ctx context.Context) error {
// MakeBucketLocation - creates a new bucket across all sets simultaneously
// even if one of the sets fail to create buckets, we proceed to undo a
// successful operation.
func (s *xlSets) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (s *xlSets) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
g := errgroup.WithNErrs(len(s.sets))
// Create buckets in parallel across all sets.
for index := range s.sets {
index := index
g.Go(func() error {
return s.sets[index].MakeBucketWithLocation(ctx, bucket, location)
return s.sets[index].MakeBucketWithLocation(ctx, bucket, location, lockEnabled)
}, index)
}
@ -657,6 +658,31 @@ func (s *xlSets) DeleteBucketSSEConfig(ctx context.Context, bucket string) error
return removeBucketSSEConfig(ctx, s, bucket)
}
// SetBucketObjectLockConfig enables/clears default object lock configuration
func (s *xlSets) SetBucketObjectLockConfig(ctx context.Context, bucket string, config *objectlock.Config) error {
return saveBucketObjectLockConfig(ctx, s, bucket, config)
}
// GetBucketObjectLockConfig - returns current defaults for object lock configuration
func (s *xlSets) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
return readBucketObjectLockConfig(ctx, s, bucket)
}
// SetBucketTagging sets bucket tags on given bucket
func (s *xlSets) SetBucketTagging(ctx context.Context, bucket string, t *tags.Tags) error {
return saveBucketTagging(ctx, s, bucket, t)
}
// GetBucketTagging get bucket tags set on given bucket
func (s *xlSets) GetBucketTagging(ctx context.Context, bucket string) (*tags.Tags, error) {
return readBucketTagging(ctx, s, bucket)
}
// DeleteBucketTagging delete bucket tags set if any.
func (s *xlSets) DeleteBucketTagging(ctx context.Context, bucket string) error {
return deleteBucketTagging(ctx, s, bucket)
}
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
func (s *xlSets) IsNotificationSupported() bool {
return s.getHashedSet("").IsNotificationSupported()
@ -717,7 +743,7 @@ func undoDeleteBucketSets(bucket string, sets []*xlObjects, errs []error) {
index := index
g.Go(func() error {
if errs[index] == nil {
return sets[index].MakeBucketWithLocation(GlobalContext, bucket, "")
return sets[index].MakeBucketWithLocation(GlobalContext, bucket, "", false)
}
return nil
}, index)

View File

@ -21,9 +21,11 @@ import (
"sort"
"github.com/minio/minio-go/v6/pkg/s3utils"
"github.com/minio/minio-go/v6/pkg/tags"
"github.com/minio/minio/cmd/logger"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/sync/errgroup"
@ -38,7 +40,7 @@ var bucketMetadataOpIgnoredErrs = append(bucketOpIgnoredErrs, errVolumeNotFound)
/// Bucket operations
// MakeBucket - make a bucket.
func (xl xlObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (xl xlObjects) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
// Verify if bucket is valid.
if err := s3utils.CheckValidBucketNameStrict(bucket); err != nil {
return BucketNameInvalid{Bucket: bucket}
@ -321,6 +323,31 @@ func (xl xlObjects) DeleteBucketSSEConfig(ctx context.Context, bucket string) er
return removeBucketSSEConfig(ctx, xl, bucket)
}
// SetBucketObjectLockConfig enables/clears default object lock configuration
func (xl xlObjects) SetBucketObjectLockConfig(ctx context.Context, bucket string, config *objectlock.Config) error {
return saveBucketObjectLockConfig(ctx, xl, bucket, config)
}
// GetBucketObjectLockConfig - returns current defaults for object lock configuration
func (xl xlObjects) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
return readBucketObjectLockConfig(ctx, xl, bucket)
}
// SetBucketTagging sets bucket tags on given bucket
func (xl xlObjects) SetBucketTagging(ctx context.Context, bucket string, t *tags.Tags) error {
return saveBucketTagging(ctx, xl, bucket, t)
}
// GetBucketTagging get bucket tags set on given bucket
func (xl xlObjects) GetBucketTagging(ctx context.Context, bucket string) (*tags.Tags, error) {
return readBucketTagging(ctx, xl, bucket)
}
// DeleteBucketTagging delete bucket tags set if any.
func (xl xlObjects) DeleteBucketTagging(ctx context.Context, bucket string) error {
return deleteBucketTagging(ctx, xl, bucket)
}
// IsNotificationSupported returns whether bucket notification is applicable for this layer.
func (xl xlObjects) IsNotificationSupported() bool {
return true

View File

@ -41,7 +41,7 @@ func TestXLParentDirIsObject(t *testing.T) {
bucketName := "testbucket"
objectName := "object"
if err = obj.MakeBucketWithLocation(GlobalContext, bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(GlobalContext, bucketName, "", false); err != nil {
t.Fatal(err)
}
objectContent := "12345"

View File

@ -181,7 +181,7 @@ func TestListOnlineDisks(t *testing.T) {
obj.DeleteObject(GlobalContext, bucket, object)
obj.DeleteBucket(GlobalContext, bucket, false)
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "")
err = obj.MakeBucketWithLocation(GlobalContext, "bucket", "", false)
if err != nil {
t.Fatalf("Failed to make a bucket %v", err)
}
@ -276,7 +276,7 @@ func TestDisksWithAllParts(t *testing.T) {
z := obj.(*xlZones)
xl := z.zones[0].sets[0]
xlDisks := xl.getDisks()
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
err = obj.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatalf("Failed to make a bucket %v", err)
}

View File

@ -44,7 +44,7 @@ func TestUndoMakeBucket(t *testing.T) {
}
bucketName := getRandomBucketName()
if err = obj.MakeBucketWithLocation(ctx, bucketName, ""); err != nil {
if err = obj.MakeBucketWithLocation(ctx, bucketName, "", false); err != nil {
t.Fatal(err)
}
z := obj.(*xlZones)
@ -88,7 +88,7 @@ func TestHealObjectCorrupted(t *testing.T) {
data := bytes.Repeat([]byte("a"), 5*1024*1024)
var opts ObjectOptions
err = objLayer.MakeBucketWithLocation(ctx, bucket, "")
err = objLayer.MakeBucketWithLocation(ctx, bucket, "", false)
if err != nil {
t.Fatalf("Failed to make a bucket - %v", err)
}
@ -233,7 +233,7 @@ func TestHealObjectXL(t *testing.T) {
data := bytes.Repeat([]byte("a"), 5*1024*1024)
var opts ObjectOptions
err = obj.MakeBucketWithLocation(ctx, bucket, "")
err = obj.MakeBucketWithLocation(ctx, bucket, "", false)
if err != nil {
t.Fatalf("Failed to make a bucket - %v", err)
}
@ -322,7 +322,7 @@ func TestHealEmptyDirectoryXL(t *testing.T) {
object := "empty-dir/"
var opts ObjectOptions
err = obj.MakeBucketWithLocation(ctx, bucket, "")
err = obj.MakeBucketWithLocation(ctx, bucket, "", false)
if err != nil {
t.Fatalf("Failed to make a bucket - %v", err)
}

View File

@ -43,7 +43,7 @@ func TestXLCleanupStaleMultipartUploads(t *testing.T) {
objectName := "object"
var opts ObjectOptions
obj.MakeBucketWithLocation(ctx, bucketName, "")
obj.MakeBucketWithLocation(ctx, bucketName, "", false)
uploadID, err := obj.NewMultipartUpload(GlobalContext, bucketName, objectName, opts)
if err != nil {
t.Fatal("Unexpected err: ", err)

View File

@ -49,7 +49,7 @@ func TestRepeatPutObjectPart(t *testing.T) {
// cleaning up of temporary test directories
defer removeRoots(disks)
err = objLayer.MakeBucketWithLocation(ctx, "bucket1", "")
err = objLayer.MakeBucketWithLocation(ctx, "bucket1", "", false)
if err != nil {
t.Fatal(err)
}
@ -96,7 +96,7 @@ func TestXLDeleteObjectBasic(t *testing.T) {
t.Fatal(err)
}
err = xl.MakeBucketWithLocation(ctx, "bucket", "")
err = xl.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatal(err)
}
@ -152,7 +152,7 @@ func TestXLDeleteObjectsXLSet(t *testing.T) {
{bucketName, "obj_4"},
}
err := xlSets.MakeBucketWithLocation(GlobalContext, bucketName, "")
err := xlSets.MakeBucketWithLocation(GlobalContext, bucketName, "", false)
if err != nil {
t.Fatal(err)
}
@ -211,7 +211,7 @@ func TestXLDeleteObjectDiskNotFound(t *testing.T) {
xl := z.zones[0].sets[0]
// Create "bucket"
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
err = obj.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatal(err)
}
@ -278,7 +278,7 @@ func TestGetObjectNoQuorum(t *testing.T) {
xl := z.zones[0].sets[0]
// Create "bucket"
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
err = obj.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatal(err)
}
@ -340,7 +340,7 @@ func TestPutObjectNoQuorum(t *testing.T) {
xl := z.zones[0].sets[0]
// Create "bucket"
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
err = obj.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatal(err)
}
@ -400,7 +400,7 @@ func TestHealing(t *testing.T) {
xl := z.zones[0].sets[0]
// Create "bucket"
err = obj.MakeBucketWithLocation(ctx, "bucket", "")
err = obj.MakeBucketWithLocation(ctx, "bucket", "", false)
if err != nil {
t.Fatal(err)
}
@ -511,7 +511,7 @@ func testObjectQuorumFromMeta(obj ObjectLayer, instanceType string, dirs []strin
xl := z.zones[0].sets[0]
xlDisks := xl.getDisks()
err := obj.MakeBucketWithLocation(GlobalContext, bucket, globalMinioDefaultRegion)
err := obj.MakeBucketWithLocation(GlobalContext, bucket, globalMinioDefaultRegion, false)
if err != nil {
t.Fatalf("Failed to make a bucket %v", err)
}

View File

@ -31,6 +31,7 @@ import (
"github.com/minio/minio/cmd/logger"
bucketsse "github.com/minio/minio/pkg/bucket/encryption"
"github.com/minio/minio/pkg/bucket/lifecycle"
objectlock "github.com/minio/minio/pkg/bucket/object/lock"
"github.com/minio/minio/pkg/bucket/policy"
"github.com/minio/minio/pkg/madmin"
"github.com/minio/minio/pkg/sync/errgroup"
@ -336,9 +337,19 @@ func undoMakeBucketZones(bucket string, zones []*xlSets, errs []error) {
// MakeBucketWithLocation - creates a new bucket across all zones simultaneously
// even if one of the sets fail to create buckets, we proceed all the successful
// operations.
func (z *xlZones) MakeBucketWithLocation(ctx context.Context, bucket, location string) error {
func (z *xlZones) MakeBucketWithLocation(ctx context.Context, bucket, location string, lockEnabled bool) error {
if z.SingleZone() {
return z.zones[0].MakeBucketWithLocation(ctx, bucket, location)
if err := z.zones[0].MakeBucketWithLocation(ctx, bucket, location, lockEnabled); err != nil {
return err
}
if lockEnabled {
meta := newBucketMetadata(bucket)
meta.LockEnabled = lockEnabled
if err := meta.save(ctx, z); err != nil {
return toObjectErr(err, bucket)
}
}
return nil
}
g := errgroup.WithNErrs(len(z.zones))
@ -347,7 +358,7 @@ func (z *xlZones) MakeBucketWithLocation(ctx context.Context, bucket, location s
for index := range z.zones {
index := index
g.Go(func() error {
return z.zones[index].MakeBucketWithLocation(ctx, bucket, location)
return z.zones[index].MakeBucketWithLocation(ctx, bucket, location, lockEnabled)
}, index)
}
@ -362,6 +373,14 @@ func (z *xlZones) MakeBucketWithLocation(ctx context.Context, bucket, location s
}
}
if lockEnabled {
meta := newBucketMetadata(bucket)
meta.LockEnabled = lockEnabled
if err := meta.save(ctx, z); err != nil {
return toObjectErr(err, bucket)
}
}
// Success.
return nil
@ -405,11 +424,11 @@ func (z *xlZones) GetObjectNInfo(ctx context.Context, bucket, object string, rs
func (z *xlZones) GetObject(ctx context.Context, bucket, object string, startOffset int64, length int64, writer io.Writer, etag string, opts ObjectOptions) error {
// Lock the object before reading.
objectLock := z.NewNSLock(ctx, bucket, object)
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err := lk.GetRLock(globalObjectTimeout); err != nil {
return err
}
defer objectLock.RUnlock()
defer lk.RUnlock()
if z.SingleZone() {
return z.zones[0].GetObject(ctx, bucket, object, startOffset, length, writer, etag, opts)
@ -428,11 +447,11 @@ func (z *xlZones) GetObject(ctx context.Context, bucket, object string, startOff
func (z *xlZones) GetObjectInfo(ctx context.Context, bucket, object string, opts ObjectOptions) (ObjectInfo, error) {
// Lock the object before reading.
objectLock := z.NewNSLock(ctx, bucket, object)
if err := objectLock.GetRLock(globalObjectTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err := lk.GetRLock(globalObjectTimeout); err != nil {
return ObjectInfo{}, err
}
defer objectLock.RUnlock()
defer lk.RUnlock()
if z.SingleZone() {
return z.zones[0].GetObjectInfo(ctx, bucket, object, opts)
@ -453,11 +472,11 @@ func (z *xlZones) GetObjectInfo(ctx context.Context, bucket, object string, opts
// PutObject - writes an object to least used erasure zone.
func (z *xlZones) PutObject(ctx context.Context, bucket string, object string, data *PutObjReader, opts ObjectOptions) (ObjectInfo, error) {
// Lock the object.
objectLock := z.NewNSLock(ctx, bucket, object)
if err := objectLock.GetLock(globalObjectTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err := lk.GetLock(globalObjectTimeout); err != nil {
return ObjectInfo{}, err
}
defer objectLock.Unlock()
defer lk.Unlock()
if z.SingleZone() {
return z.zones[0].PutObject(ctx, bucket, object, data, opts)
@ -480,11 +499,11 @@ func (z *xlZones) PutObject(ctx context.Context, bucket string, object string, d
func (z *xlZones) DeleteObject(ctx context.Context, bucket string, object string) error {
// Acquire a write lock before deleting the object.
objectLock := z.NewNSLock(ctx, bucket, object)
if err := objectLock.GetLock(globalOperationTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err := lk.GetLock(globalOperationTimeout); err != nil {
return err
}
defer objectLock.Unlock()
defer lk.Unlock()
if z.SingleZone() {
return z.zones[0].DeleteObject(ctx, bucket, object)
@ -531,11 +550,11 @@ func (z *xlZones) CopyObject(ctx context.Context, srcBucket, srcObject, destBuck
// Check if this request is only metadata update.
cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(destBucket, destObject))
if !cpSrcDstSame {
objectLock := z.NewNSLock(ctx, destBucket, destObject)
if err := objectLock.GetLock(globalObjectTimeout); err != nil {
lk := z.NewNSLock(ctx, destBucket, destObject)
if err := lk.GetLock(globalObjectTimeout); err != nil {
return objInfo, err
}
defer objectLock.Unlock()
defer lk.Unlock()
}
if z.SingleZone() {
@ -1110,11 +1129,11 @@ func (z *xlZones) CompleteMultipartUpload(ctx context.Context, bucket, object, u
// Hold namespace to complete the transaction, only hold
// if uploadID can be held exclusively.
objectLock := z.NewNSLock(ctx, bucket, object)
if err = objectLock.GetLock(globalOperationTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err = lk.GetLock(globalOperationTimeout); err != nil {
return objInfo, err
}
defer objectLock.Unlock()
defer lk.Unlock()
if z.SingleZone() {
return z.zones[0].CompleteMultipartUpload(ctx, bucket, object, uploadID, uploadedParts, opts)
@ -1201,6 +1220,31 @@ func (z *xlZones) SetBucketSSEConfig(ctx context.Context, bucket string, config
return saveBucketSSEConfig(ctx, z, bucket, config)
}
// SetBucketObjectLockConfig enables/clears default object lock configuration
func (z *xlZones) SetBucketObjectLockConfig(ctx context.Context, bucket string, config *objectlock.Config) error {
return saveBucketObjectLockConfig(ctx, z, bucket, config)
}
// GetBucketObjectLockConfig - returns current defaults for object lock configuration
func (z *xlZones) GetBucketObjectLockConfig(ctx context.Context, bucket string) (*objectlock.Config, error) {
return readBucketObjectLockConfig(ctx, z, bucket)
}
// SetBucketTagging sets bucket tags on given bucket
func (z *xlZones) SetBucketTagging(ctx context.Context, bucket string, t *tags.Tags) error {
return saveBucketTagging(ctx, z, bucket, t)
}
// GetBucketTagging get bucket tags set on given bucket
func (z *xlZones) GetBucketTagging(ctx context.Context, bucket string) (*tags.Tags, error) {
return readBucketTagging(ctx, z, bucket)
}
// DeleteBucketTagging delete bucket tags set if any.
func (z *xlZones) DeleteBucketTagging(ctx context.Context, bucket string) error {
return deleteBucketTagging(ctx, z, bucket)
}
// DeleteBucketSSEConfig deletes bucket encryption config on given bucket
func (z *xlZones) DeleteBucketSSEConfig(ctx context.Context, bucket string) error {
return removeBucketSSEConfig(ctx, z, bucket)
@ -1245,7 +1289,8 @@ func (z *xlZones) DeleteBucket(ctx context.Context, bucket string, forceDelete b
errs := g.Wait()
if forceDelete {
// For any write quorum failure, we undo all the delete
// buckets operation by creating all the buckets again.
for _, err := range errs {
if err != nil {
if _, ok := err.(InsufficientWriteQuorum); ok {
@ -1256,20 +1301,6 @@ func (z *xlZones) DeleteBucket(ctx context.Context, bucket string, forceDelete b
}
}
return nil
}
// For any write quorum failure, we undo all the delete buckets operation
// by creating all the buckets again.
for _, err := range errs {
if err != nil {
if _, ok := err.(InsufficientWriteQuorum); ok {
undoDeleteBucketZones(bucket, z.zones, errs)
}
return err
}
}
// Success.
return nil
}
@ -1283,7 +1314,7 @@ func undoDeleteBucketZones(bucket string, zones []*xlSets, errs []error) {
index := index
g.Go(func() error {
if errs[index] == nil {
return zones[index].MakeBucketWithLocation(GlobalContext, bucket, "")
return zones[index].MakeBucketWithLocation(GlobalContext, bucket, "", false)
}
return nil
}, index)
@ -1297,17 +1328,30 @@ func undoDeleteBucketZones(bucket string, zones []*xlSets, errs []error) {
// that all buckets are present on all zones.
func (z *xlZones) ListBuckets(ctx context.Context) (buckets []BucketInfo, err error) {
if z.SingleZone() {
return z.zones[0].ListBuckets(ctx)
}
buckets, err = z.zones[0].ListBuckets(ctx)
} else {
for _, zone := range z.zones {
buckets, err := zone.ListBuckets(ctx)
buckets, err = zone.ListBuckets(ctx)
if err != nil {
logger.LogIf(ctx, err)
continue
}
return buckets, nil
break
}
return buckets, InsufficientReadQuorum{}
}
if err != nil {
return nil, err
}
for i := range buckets {
meta, err := loadBucketMetadata(ctx, z, buckets[i].Name)
if err == nil {
buckets[i].Created = meta.Created
}
if err != errMetaDataConverted {
logger.LogIf(ctx, err)
}
}
return buckets, nil
}
func (z *xlZones) ReloadFormat(ctx context.Context, dryRun bool) error {
@ -1488,11 +1532,11 @@ func (z *xlZones) HealObjects(ctx context.Context, bucket, prefix string, opts m
func (z *xlZones) HealObject(ctx context.Context, bucket, object string, opts madmin.HealOpts) (madmin.HealResultItem, error) {
// Lock the object before healing. Use read lock since healing
// will only regenerate parts & xl.json of outdated disks.
objectLock := z.NewNSLock(ctx, bucket, object)
if err := objectLock.GetRLock(globalHealingTimeout); err != nil {
lk := z.NewNSLock(ctx, bucket, object)
if err := lk.GetRLock(globalHealingTimeout); err != nil {
return madmin.HealResultItem{}, err
}
defer objectLock.RUnlock()
defer lk.RUnlock()
if z.SingleZone() {
return z.zones[0].HealObject(ctx, bucket, object, opts)

View File

@ -1,6 +1,6 @@
#!/bin/bash
#
# Mint (C) 2017, 2018 Minio, Inc.
# Mint (C) 2017-2020 Minio, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -69,24 +69,6 @@ function make_bucket() {
return $rv
}
function make_bucket_with_lock() {
# Make bucket
bucket_name="awscli-mint-test-bucket-$RANDOM"
function="${AWS} s3api create-bucket --bucket ${bucket_name} --object-lock-enabled-for-bucket"
# execute the test
out=$($function 2>&1)
rv=$?
# if command is successful print bucket_name or print error
if [ $rv -eq 0 ]; then
echo "${bucket_name}"
else
echo "${out}"
fi
return $rv
}
function delete_bucket() {
# Delete bucket
function="${AWS} s3 rb s3://${1} --force"
@ -688,7 +670,7 @@ function test_copy_object_storage_class() {
out=$($function 2>&1)
rv=$?
# if this functionality is not implemented return right away.
if [ $rv -eq 255 ]; then
if [ $rv -ne 0 ]; then
if echo "$out" | greq -q "NotImplemented"; then
${AWS} s3 rb s3://"${bucket_name}" --force > /dev/null 2>&1
return 0
@ -758,7 +740,7 @@ function test_copy_object_storage_class_same() {
out=$($function 2>&1)
rv=$?
# if this functionality is not implemented return right away.
if [ $rv -eq 255 ]; then
if [ $rv -ne 0 ]; then
if echo "$out" | greq -q "NotImplemented"; then
${AWS} s3 rb s3://"${bucket_name}" --force > /dev/null 2>&1
return 0
@ -1591,9 +1573,22 @@ function test_legal_hold() {
# log start time
start_time=$(get_time)
function="make_bucket_with_lock"
bucket_name=$(make_bucket_with_lock)
# Make bucket
bucket_name="awscli-mint-test-bucket-$RANDOM"
function="${AWS} s3api create-bucket --bucket ${bucket_name} --object-lock-enabled-for-bucket"
# execute the test
out=$($function 2>&1)
rv=$?
if [ $rv -ne 0 ]; then
# if this functionality is not implemented return right away.
if echo "$out" | greq -q "NotImplemented"; then
${AWS} s3 rb s3://"${bucket_name}" --force > /dev/null 2>&1
return 0
fi
fi
# if make bucket succeeds upload a file
if [ $rv -eq 0 ]; then
function="${AWS} s3api put-object --body ${MINT_DATA_DIR}/datafile-1-kB --bucket ${bucket_name} --key datafile-1-kB --object-lock-legal-hold-status ON"

View File

@ -24,7 +24,6 @@ import (
"io"
"net/http"
"strings"
"sync"
"time"
"github.com/beevik/ntp"
@ -158,41 +157,6 @@ func (r Retention) Retain(created time.Time) bool {
return created.Add(r.Validity).After(t)
}
// BucketObjectLockConfig - map of bucket and retention configuration.
type BucketObjectLockConfig struct {
sync.RWMutex
retentionMap map[string]*Retention
}
// Set - set retention configuration.
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) {
config.RLock()
defer config.RUnlock()
r, ok = config.retentionMap[bucketName]
return r, ok
}
// Remove - removes retention configuration.
func (config *BucketObjectLockConfig) Remove(bucketName string) {
config.Lock()
delete(config.retentionMap, bucketName)
config.Unlock()
}
// NewBucketObjectLockConfig returns initialized BucketObjectLockConfig
func NewBucketObjectLockConfig() *BucketObjectLockConfig {
return &BucketObjectLockConfig{
retentionMap: make(map[string]*Retention),
}
}
// DefaultRetention - default retention configuration.
type DefaultRetention struct {
XMLName xml.Name `xml:"DefaultRetention"`