mirror of
https://github.com/minio/minio.git
synced 2024-12-23 21:55:53 -05:00
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:
parent
6885c72f32
commit
a1de9cec58
@ -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:
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,24 +562,16 @@ 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 {
|
||||
ret := &objectlock.Retention{}
|
||||
globalBucketObjectLockConfig.Set(bucket, ret)
|
||||
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, ret)
|
||||
}
|
||||
if objectLockEnabled && !globalIsGateway {
|
||||
ret := &objectlock.Retention{}
|
||||
globalBucketObjectLockSys.Set(bucket, ret)
|
||||
globalNotificationSys.PutBucketObjectLockConfig(ctx, bucket, ret)
|
||||
}
|
||||
|
||||
// Make sure to add Location information here only for bucket
|
||||
@ -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,28 +1064,16 @@ func (api objectAPIHandlers) GetBucketObjectLockConfigHandler(w http.ResponseWri
|
||||
return
|
||||
}
|
||||
|
||||
meta, err := loadBucketMetadata(ctx, objectAPI, bucket)
|
||||
if err != nil && err != errMetaDataConverted {
|
||||
lkCfg, err := objectAPI.GetBucketObjectLockConfig(ctx, bucket)
|
||||
if 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
|
||||
}
|
||||
|
||||
configFile := path.Join(bucketConfigPrefix, bucket, objectLockConfig)
|
||||
configData, err := readConfig(ctx, objectAPI, configFile)
|
||||
configData, err := xml.Marshal(lkCfg)
|
||||
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 {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
|
||||
// Write success response.
|
||||
@ -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, aerr, r.URL, guessIsBrowserReq(r))
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -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 err
|
||||
return nil
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
61
cmd/bucket-tagging.go
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
84
cmd/fs-v1.go
84
cmd/fs-v1.go
@ -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(),
|
||||
Name: fi.Name(),
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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{})
|
||||
|
@ -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.
|
||||
|
@ -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"
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,10 +215,9 @@ 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()
|
||||
|
||||
globalBucketQuotaSys *BucketQuotaSys
|
||||
globalBucketStorageCache bucketStorageCache
|
||||
globalBucketObjectLockSys *BucketObjectLockSys
|
||||
globalBucketQuotaSys *BucketQuotaSys
|
||||
globalBucketStorageCache bucketStorageCache
|
||||
|
||||
// Disk cache drives
|
||||
globalCacheConfig cache.Config
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
144
cmd/xl-zones.go
144
cmd/xl-zones.go
@ -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,27 +1289,14 @@ func (z *xlZones) DeleteBucket(ctx context.Context, bucket string, forceDelete b
|
||||
|
||||
errs := g.Wait()
|
||||
|
||||
if forceDelete {
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
if _, ok := err.(InsufficientWriteQuorum); ok {
|
||||
undoDeleteBucketZones(bucket, z.zones, errs)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// For any write quorum failure, we undo all the delete buckets operation
|
||||
// by creating all the buckets again.
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
for _, zone := range z.zones {
|
||||
buckets, err := zone.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
buckets, err = z.zones[0].ListBuckets(ctx)
|
||||
} else {
|
||||
for _, zone := range z.zones {
|
||||
buckets, err = zone.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
logger.LogIf(ctx, err)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
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)
|
||||
|
@ -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"
|
||||
|
@ -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"`
|
||||
|
Loading…
Reference in New Issue
Block a user