为了账号安全,请及时绑定邮箱和手机立即绑定

如何强制删除 S3 存储桶中的所有对象版本,然后最终使用 aws-sdk-go 删除整个存储桶?

如何强制删除 S3 存储桶中的所有对象版本,然后最终使用 aws-sdk-go 删除整个存储桶?

Go
泛舟湖上清波郎朗 2022-05-18 16:08:31
我有一个启用了版本控制的 S3 存储桶。存储桶几乎没有具有版本的文件。我编写了一个示例 golang 程序,它可以执行以下操作:GetBucketVersioning - 它能够获取存储桶版本控制状态,即启用ListObjects - 它能够列出存储桶对象DeleteObjects - 它能够删除存储桶对象(但它只是将“删除标记”添加到每个对象的最新版本。对象的版本历史仍然保持未删除)DeleteBucket:此操作失败并显示错误消息:“BucketNotEmpty:您尝试删除的存储桶不为空。您必须删除存储桶中的所有版本。”您能否建议如何强制删除S3 存储桶中所有对象的所有版本,以便我最终可以使用 删除整个存储桶aws-sdk-go?
查看完整描述

2 回答

?
慕斯709654

TA贡献1840条经验 获得超5个赞

根据文档,DeleteBucket状态,

必须先删除存储桶中的所有对象(包括所有对象版本和删除标记),然后才能删除存储桶本身。

现在,要从启用版本控制的存储桶中删除版本,我们可以

  1. 使用DeleteObject,其中指出,

要删除特定版本,您必须是存储桶所有者并且必须使用版本 ID 子资源。使用此子资源会永久删除该版本。

  1. 使用DeleteObjects,类似地指出,

在 XML 中,如果要从启用版本控制的存储桶中删除对象的特定版本,您可以提供对象键名称和可选的版本 ID。

在使用以下命令(先决条件 - Docker、Docker Compose、AWS CLI)创建存储桶并使用包含版本的文件填充它之后,我整理了一个示例程序,并针对LocalStack进行了测试。

curl -O https://raw.githubusercontent.com/localstack/localstack/master/docker-compose.yml

export SERVICES="s3"

docker-compose up


export AWS_ACCESS_KEY_ID="test"

export AWS_SECRET_ACCESS_KEY="test"

export AWS_DEFAULT_REGION="us-east-1"

aws --endpoint-url=http://localhost:4566 s3 mb s3://testbucket

aws --endpoint-url=http://localhost:4566 s3api put-bucket-versioning --bucket testbucket --versioning-configuration Status=Enabled

for i in 1 2 3; do

    aws --endpoint-url=http://localhost:4566 s3 cp main.go s3://testbucket/main.go

    aws --endpoint-url=http://localhost:4566 s3 cp go.mod s3://testbucket/go.mod

done


aws --endpoint-url=http://localhost:4566 s3api list-object-versions --bucket testbucket

运行前设置以下环境变量


export AWS_ENDPOINT="http://localhost:4566"

export S3_BUCKET="testbucket"

package main


import (

    "context"

    "fmt"

    "log"

    "os"


    "github.com/aws/aws-sdk-go-v2/aws"

    "github.com/aws/aws-sdk-go-v2/config"

    "github.com/aws/aws-sdk-go-v2/service/s3"

    "github.com/aws/aws-sdk-go-v2/service/s3/types"

)


type s3Client struct {

    *s3.Client

}


func main() {

    awsEndpoint := os.Getenv("AWS_ENDPOINT")

    bucketName := os.Getenv("S3_BUCKET")


    cfg, err := config.LoadDefaultConfig(context.TODO(),

        config.WithEndpointResolverWithOptions(aws.EndpointResolverWithOptionsFunc(

            func(service, region string, options ...interface{}) (aws.Endpoint, error) {

                return aws.Endpoint{

                    URL:               awsEndpoint,

                    HostnameImmutable: true,

                }, nil

            })),

    )

    if err != nil {

        log.Fatalf("Cannot load the AWS configs: %s", err)

    }


    serviceClient := s3.NewFromConfig(cfg)


    client := &s3Client{

        Client: serviceClient,

    }


    fmt.Printf(">>> Bucket: %s\n", bucketName)


    objects, err := client.listObjects(bucketName)

    if err != nil {

        log.Fatal(err)

    }

    if len(objects) > 0 {

        fmt.Printf(">>> List objects in the bucket: \n")

        for _, object := range objects {

            fmt.Printf("%s\n", object)

        }

    } else {

        fmt.Printf(">>> No objects in the bucket.\n")

    }


    if client.versioningEnabled(bucketName) {

        fmt.Printf(">>> Versioning is enabled.\n")

        objectVersions, err := client.listObjectVersions(bucketName)

        if err != nil {

            log.Fatal(err)

        }

        if len(objectVersions) > 0 {

            fmt.Printf(">>> List objects with versions: \n")

            for key, versions := range objectVersions {

                fmt.Printf("%s: ", key)

                for _, version := range versions {

                    fmt.Printf("\n\t%s ", version)

                }

                fmt.Println()

            }

        }


        if len(objectVersions) > 0 {

            fmt.Printf(">>> Delete objects with versions.\n")

            if err := client.deleteObjects(bucketName, objectVersions); err != nil {

                log.Fatal(err)

            }


            objectVersions, err = client.listObjectVersions(bucketName)

            if err != nil {

                log.Fatal(err)

            }

            if len(objectVersions) > 0 {

                fmt.Printf(">>> List objects with versions after deletion: \n")

                for key, version := range objectVersions {

                    fmt.Printf("%s: %s\n", key, version)

                }

            } else {

                fmt.Printf(">>> No objects in the bucket after deletion.\n")

            }

        }

    }


    fmt.Printf(">>> Delete the bucket.\n")

    if err := client.deleteBucket(bucketName); err != nil {

        log.Fatal(err)

    }


}


func (c *s3Client) versioningEnabled(bucket string) bool {

    output, err := c.GetBucketVersioning(context.TODO(), &s3.GetBucketVersioningInput{

        Bucket: aws.String(bucket),

    })

    if err != nil {

        return false

    }

    return output.Status == "Enabled"

}


func (c *s3Client) listObjects(bucket string) ([]string, error) {

    var objects []string

    output, err := c.ListObjectsV2(context.TODO(), &s3.ListObjectsV2Input{

        Bucket: aws.String(bucket),

    })

    if err != nil {

        return nil, err

    }


    for _, object := range output.Contents {

        objects = append(objects, aws.ToString(object.Key))

    }


    return objects, nil

}


func (c *s3Client) listObjectVersions(bucket string) (map[string][]string, error) {

    var objectVersions = make(map[string][]string)

    output, err := c.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{

        Bucket: aws.String(bucket),

    })

    if err != nil {

        return nil, err

    }


    for _, object := range output.Versions {

        if _, ok := objectVersions[aws.ToString(object.Key)]; ok {

            objectVersions[aws.ToString(object.Key)] = append(objectVersions[aws.ToString(object.Key)], aws.ToString(object.VersionId))

        } else {

            objectVersions[aws.ToString(object.Key)] = []string{aws.ToString(object.VersionId)}

        }

    }


    return objectVersions, err

}


func (c *s3Client) deleteObjects(bucket string, objectVersions map[string][]string) error {

    var identifiers []types.ObjectIdentifier

    for key, versions := range objectVersions {

        for _, version := range versions {

            identifiers = append(identifiers, types.ObjectIdentifier{

                Key:       aws.String(key),

                VersionId: aws.String(version),

            })

        }

    }


    _, err := c.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{

        Bucket: aws.String(bucket),

        Delete: &types.Delete{

            Objects: identifiers,

        },

    })

    if err != nil {

        return err

    }

    return nil

}


func (c *s3Client) deleteBucket(bucket string) error {

    _, err := c.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{

        Bucket: aws.String(bucket),

    })

    if err != nil {

        return err

    }


    return nil

}

请注意,在listObjectVersions方法中,我将VersionIds 与Keys 映射。


    for _, object := range output.Versions {

        if _, ok := objectVersions[aws.ToString(object.Key)]; ok {

            objectVersions[aws.ToString(object.Key)] = append(objectVersions[aws.ToString(object.Key)], aws.ToString(object.VersionId))

        } else {

            objectVersions[aws.ToString(object.Key)] = []string{aws.ToString(object.VersionId)}

        }

    }

然后在deleteObjects方法中,当传递ObjectIdentifiers 时,我为一个对象的所有版本传递了 sKey和ObjectIds。


    for key, versions := range objectVersions {

        for _, version := range versions {

            identifiers = append(identifiers, types.ObjectIdentifier{

                Key:       aws.String(key),

                VersionId: aws.String(version),

            })

        }

    }


查看完整回答
反对 回复 2022-05-18
?
繁华开满天机

TA贡献1816条经验 获得超4个赞

使用 golang sdk 这似乎是不可能的。他们没有实现删除版本功能。



查看完整回答
反对 回复 2022-05-18
  • 2 回答
  • 0 关注
  • 282 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号