簡體   English   中英

如何強制刪除 S3 存儲桶中對象的所有版本,然后最終使用 aws-sdk-go 刪除整個存儲桶?

[英]How to force delete all versions of objects in S3 bucket and then eventually delete the entire bucket using aws-sdk-go?

我有一個啟用了版本控制的 S3 存儲桶。 存儲桶中幾乎沒有具有版本的文件。 我編寫了一個示例 golang 程序,它可以執行以下操作:

  • GetBucketVersioning - 可以獲取存儲桶版本控制狀態,即已啟用
  • ListObjects - 能夠列出存儲桶對象
  • DeleteObjects - 它能夠刪除存儲桶對象(但它只是將“刪除標記”添加到每個對象的最新版本中。對象的版本歷史仍然未被刪除)
  • DeleteBucket :此操作失敗並顯示錯誤消息:

“BucketNotEmpty:您嘗試刪除的存儲桶不為空。
您必須刪除存儲桶中的所有版本。”

您能否建議如何強制刪除 S3 存儲桶中所有對象的所有版本,以便我最終可以使用aws-sdk-go刪除整個存儲桶?

使用 golang sdk 這似乎是不可能的。 他們沒有實現刪除版本功能。

根據文檔, 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方法中,我將VersionIdKey映射。

    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方法中,當傳遞ObjectIdentifier ,我傳遞了一個對象的所有版本的KeyObjectId

    for key, versions := range objectVersions {
        for _, version := range versions {
            identifiers = append(identifiers, types.ObjectIdentifier{
                Key:       aws.String(key),
                VersionId: aws.String(version),
            })
        }
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM