Files
seaweedfs/weed/shell/command_s3_bucket_delete.go
T
Chris LuandGitHub 11791fad6a filer: resolve the collection a bucket delete drops (#11439)
* filer: resolve the collection a bucket delete drops

A bucket delete dropped the collection named after the bucket, which
assumes bucket name is collection name. With a collection rule the
write path honors, deleting the bucket either orphaned its collection
or, when a bucket was named after a shared collection, removed volumes
other buckets still write to.

Resolve the collection through the same rule chain the write path uses
and drop it only when no other bucket resolves there too. A listing
failure keeps the collection, the safe side of an unknown.

* filer: prove collection exclusivity across all paths before dropping it

The sibling-bucket scan missed every non-bucket writer: a broad rule like
'/' or '/buckets/', a rule under a surviving bucket, or a rule on an
unrelated path can route into the same collection. Check every storage
rule's prefix instead, and mirror the grouped gateway's explicit
<group>_<bucket> collection, which otherwise resolves a rule-named
collection the bucket never wrote to.

* s3: let the filer own the collection decision on bucket delete

Both entry points deleted a name-derived collection around the filer's
own resolved delete, bypassing its exclusivity check and wiping sibling
data. The filer now resolves the collection a bucket actually used,
including the grouped form.

* filer: keep a collection the default write route also uses

Rule-less writes outside buckets land in the filer's default collection,
so a bucket resolving there shares it with them.
2026-09-25 07:30:42 +08:00

70 lines
1.6 KiB
Go

package shell
import (
"context"
"flag"
"fmt"
"io"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_objectlock"
)
func init() {
Commands = append(Commands, &commandS3BucketDelete{})
}
type commandS3BucketDelete struct {
}
func (c *commandS3BucketDelete) Name() string {
return "s3.bucket.delete"
}
func (c *commandS3BucketDelete) Help() string {
return `delete a bucket by a given name
s3.bucket.delete -name <bucket_name>
`
}
func (c *commandS3BucketDelete) HasTag(CommandTag) bool {
return false
}
func (c *commandS3BucketDelete) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
bucketCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
bucketName := bucketCommand.String("name", "", "bucket name")
if err = bucketCommand.Parse(args); err != nil {
return nil
}
if *bucketName == "" {
return fmt.Errorf("empty bucket name")
}
_, parseErr := commandEnv.parseUrl(findInputDirectory(bucketCommand.Args()))
if parseErr != nil {
return parseErr
}
var filerBucketsPath string
filerBucketsPath, err = readFilerBucketsPath(commandEnv)
if err != nil {
return fmt.Errorf("read buckets: %w", err)
}
// Check if bucket has Object Lock enabled and if there are locked objects
ctx := context.Background()
err = commandEnv.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
return s3_objectlock.CheckBucketForLockedObjects(ctx, client, filerBucketsPath, *bucketName)
})
if err != nil {
return err
}
return filer_pb.Remove(ctx, commandEnv, filerBucketsPath, *bucketName, false, true, true, false, nil)
}