From 5a9a898ba2a5fb55219159a26dfbb4e91887b755 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 27 Apr 2022 04:44:07 -0700 Subject: [PATCH] allow forcibly creating metadata on buckets (#14820) introduce x-minio-force-create environment variable to force create a bucket and its metadata as required, it is useful in some situations when bucket metadata needs recovery. --- cmd/admin-handlers-site-replication.go | 2 ++ cmd/bucket-handlers.go | 23 +++++++++++++++++++---- cmd/erasure-bucket.go | 5 +++++ cmd/object-api-interface.go | 1 + cmd/site-replication.go | 3 +++ internal/http/headers.go | 6 ++++++ 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/cmd/admin-handlers-site-replication.go b/cmd/admin-handlers-site-replication.go index d161703c5..059a97b4c 100644 --- a/cmd/admin-handlers-site-replication.go +++ b/cmd/admin-handlers-site-replication.go @@ -115,10 +115,12 @@ func (a adminAPIHandlers) SRPeerBucketOps(w http.ResponseWriter, r *http.Request case madmin.MakeWithVersioningBktOp: _, isLockEnabled := r.Form["lockEnabled"] _, isVersioningEnabled := r.Form["versioningEnabled"] + _, isForceCreate := r.Form["forceCreate"] opts := BucketOptions{ Location: r.Form.Get("location"), LockEnabled: isLockEnabled, VersioningEnabled: isVersioningEnabled, + ForceCreate: isForceCreate, } err = globalSiteReplicationSys.PeerBucketMakeWithVersioningHandler(ctx, bucket, opts) case madmin.ConfigureReplBktOp: diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 8d9acf20d..b95834817 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -706,13 +706,27 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req bucket := vars["bucket"] objectLockEnabled := false - if vs, found := r.Header[http.CanonicalHeaderKey("x-amz-bucket-object-lock-enabled")]; found { - v := strings.ToLower(strings.Join(vs, "")) - if v != "true" && v != "false" { + if vs := r.Header.Get(xhttp.AmzObjectLockEnabled); len(vs) > 0 { + v := strings.ToLower(vs) + switch v { + case "true", "false": + objectLockEnabled = v == "true" + default: + writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) + return + } + } + + forceCreate := false + if vs := r.Header.Get(xhttp.MinIOForceCreate); len(vs) > 0 { + v := strings.ToLower(vs) + switch v { + case "true", "false": + forceCreate = v == "true" + default: writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) return } - objectLockEnabled = v == "true" } if s3Error := checkRequestAuthType(ctx, r, policy.CreateBucketAction, bucket, ""); s3Error != ErrNone { @@ -737,6 +751,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req opts := BucketOptions{ Location: location, LockEnabled: objectLockEnabled, + ForceCreate: forceCreate, } if globalDNSConfig != nil { diff --git a/cmd/erasure-bucket.go b/cmd/erasure-bucket.go index 058eeeb58..c403c5dc8 100644 --- a/cmd/erasure-bucket.go +++ b/cmd/erasure-bucket.go @@ -55,6 +55,11 @@ func (er erasureObjects) MakeBucketWithLocation(ctx context.Context, bucket stri g.Go(func() error { if storageDisks[index] != nil { if err := storageDisks[index].MakeVol(ctx, bucket); err != nil { + if opts.ForceCreate && errors.Is(err, errVolumeExists) { + // No need to return error when force create was + // requested. + return nil + } if !errors.Is(err, errVolumeExists) { logger.LogIf(ctx, err) } diff --git a/cmd/object-api-interface.go b/cmd/object-api-interface.go index 94d49cc77..9dfa8ccb4 100644 --- a/cmd/object-api-interface.go +++ b/cmd/object-api-interface.go @@ -98,6 +98,7 @@ type BucketOptions struct { Location string LockEnabled bool VersioningEnabled bool + ForceCreate bool // Create buckets even if they are already created. } // DeleteBucketOptions provides options for DeleteBucket calls. diff --git a/cmd/site-replication.go b/cmd/site-replication.go index c5addb40f..2d6252b8f 100644 --- a/cmd/site-replication.go +++ b/cmd/site-replication.go @@ -625,6 +625,9 @@ func (c *SiteReplicationSys) MakeBucketHook(ctx context.Context, bucket string, if opts.VersioningEnabled { optsMap["versioningEnabled"] = "true" } + if opts.ForceCreate { + optsMap["forceCreate"] = "true" + } // Create bucket and enable versioning on all peers. makeBucketConcErr := c.concDo( diff --git a/internal/http/headers.go b/internal/http/headers.go index 5b06f4cae..294dad0df 100644 --- a/internal/http/headers.go +++ b/internal/http/headers.go @@ -91,6 +91,9 @@ const ( AmzBucketReplicationStatus = "X-Amz-Replication-Status" AmzSnowballExtract = "X-Amz-Meta-Snowball-Auto-Extract" + // Object lock enabled + AmzObjectLockEnabled = "x-amz-bucket-object-lock-enabled" + // Multipart parts count AmzMpPartsCount = "x-amz-mp-parts-count" @@ -144,6 +147,9 @@ const ( // Delete special flag to force delete a bucket or a prefix MinIOForceDelete = "x-minio-force-delete" + // Create special flag to force create a bucket + MinIOForceCreate = "x-minio-force-create" + // Header indicates if the mtime should be preserved by client MinIOSourceMTime = "x-minio-source-mtime"