Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(obj): add force_destroy option to bucket resource #1087

Merged
merged 17 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/resources/object_bucket.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The following arguments are supported:
* `region` - (Optional) The [region](https://developers.scaleway.com/en/quickstart/#region-definition) in which the bucket should be created.
* `versioning` - (Optional) A state of [versioning](https://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html) (documented below)
* `cors_rule` - (Optional) A rule of [Cross-Origin Resource Sharing](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html) (documented below).
* `force_destroy` - (Optional) Enable deletion of objects in bucket before destroying

The `CORS` object supports the following:

Expand Down
33 changes: 33 additions & 0 deletions scaleway/helpers_object.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scaleway

import (
"context"
"errors"
"fmt"
"net/http"
Expand Down Expand Up @@ -195,3 +196,35 @@ func expandBucketCORS(rawCors []interface{}, bucket string) []*s3.CORSRule {
}
return rules
}

func deleteS3Object(conn *s3.S3, bucketName string, object *s3.Object) error {
_, err := conn.DeleteObject(&s3.DeleteObjectInput{
Bucket: scw.StringPtr(bucketName),
Key: object.Key,
})
return err
}

func deleteS3Objects(ctx context.Context, conn *s3.S3, bucketName string) error {
var err error
listInput := &s3.ListObjectsInput{
Bucket: scw.StringPtr(bucketName),
}
listErr := conn.ListObjectsPagesWithContext(ctx, listInput, func(page *s3.ListObjectsOutput, lastPage bool) bool {
for _, object := range page.Contents {
err = deleteS3Object(conn, bucketName, object)
Codelax marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
err = fmt.Errorf("failed to delete S3 object: %s", err)
Monitob marked this conversation as resolved.
Show resolved Hide resolved
return false
}
}
return true
})
if listErr != nil {
return fmt.Errorf("error listing S3 objects: %s", err)
}
if err != nil {
return err
}
return nil
}
13 changes: 13 additions & 0 deletions scaleway/resource_object_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ func resourceScalewayObjectBucket() *schema.Resource {
},
},
},
"force_destroy": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Description: "Delete objects in bucket",
},
"region": regionSchema(),
"versioning": {
Type: schema.TypeList,
Expand Down Expand Up @@ -275,6 +281,13 @@ func resourceScalewayObjectBucketDelete(ctx context.Context, d *schema.ResourceD
return diag.FromErr(err)
Codelax marked this conversation as resolved.
Show resolved Hide resolved
}

if d.Get("force_destroy").(bool) {
err = deleteS3Objects(ctx, s3Client, bucketName)
Codelax marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return diag.FromErr(err)
}
}

_, err = s3Client.DeleteBucketWithContext(ctx, &s3.DeleteBucketInput{
Bucket: scw.StringPtr(bucketName),
})
Expand Down
59 changes: 59 additions & 0 deletions scaleway/resource_object_bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,62 @@ func testAccCheckScalewayObjectBucketExists(tt *TestTools, n string) resource.Te
return nil
}
}

func TestAccScalewayObjectBucketDestroy_force(t *testing.T) {
if !*UpdateCassettes {
t.Skip("Skipping ObjectStorage test as this kind of resource can't be deleted before 24h")
}
tt := NewTestTools(t)
defer tt.Cleanup()

resourceName := "scaleway_object_bucket.bucket"
bucketName := sdkacctest.RandomWithPrefix("test-acc-scaleway-object-bucket-force")

addObjectToBucket := func(tt *TestTools, n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("not found: %s", n)
}

conn, err := newS3ClientFromMeta(tt.Meta)
if err != nil {
return err
}
_, err = conn.PutObject(&s3.PutObjectInput{
Bucket: scw.StringPtr(rs.Primary.Attributes["name"]),
Key: scw.StringPtr("test-file"),
})
if err != nil {
return fmt.Errorf("failed to put object in test bucket: %s", err)
}
conn.PutObject(&s3.PutObjectInput{
Bucket: scw.StringPtr(rs.Primary.Attributes["name"]),
Key: scw.StringPtr("folder/test-file-in-folder"),
})
if err != nil {
return fmt.Errorf("failed to put object in test bucket sub folder: %s", err)
}
return nil
}
}

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProviderFactories: tt.ProviderFactories,
CheckDestroy: testAccCheckScalewayObjectBucketDestroy(tt),
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(`
resource "scaleway_object_bucket" "bucket" {
name = %[1]q
force_destroy = true
}`, bucketName),
Check: resource.ComposeTestCheckFunc(
testAccCheckScalewayObjectBucketExists(tt, resourceName),
addObjectToBucket(tt, resourceName),
),
},
},
})
}
39 changes: 39 additions & 0 deletions scaleway/testdata/object-bucket-destroy-force-delete.cassette.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
version: 1
interactions:
- request:
body: <CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LocationConstraint>fr-par</LocationConstraint></CreateBucketConfiguration>
form: {}
headers:
Authorization:
- AWS4-HMAC-SHA256 Credential=SCWK721NRK9BFRHW1CS7/20220214/fr-par/s3/aws4_request,
SignedHeaders=content-length;host;x-amz-acl;x-amz-content-sha256;x-amz-date,
Signature=7232095d12d75734c582f0378e2c9fb21b665e610b044333a079225750077efd
Content-Length:
- "150"
User-Agent:
- aws-sdk-go/1.42.48 (go1.17.6; linux; amd64)
X-Amz-Acl:
- private
X-Amz-Content-Sha256:
- 2cb57fad7b7168921a4c94426cfcb9ee2953f126430595df844e22d50f029060
X-Amz-Date:
- 20220214T110416Z
url: https://s3.fr-par.scw.cloud/test-acc-scaleway-object-bucket-destroy-force-delete-8205513653202540679
method: PUT
response:
body: |-
<?xml version='1.0' encoding='UTF-8'?>
<Error><Code>InvalidBucketName</Code><Message>The specified bucket is not valid.</Message><RequestId>tx6293f1e3063a45da8691f-00620a3736</RequestId><BucketName>test-acc-scaleway-object-bucket-destroy-force-delete-8205513653202540679</BucketName></Error>
headers:
Content-Type:
- application/xml
Date:
- Mon, 14 Feb 2022 11:04:22 GMT
X-Amz-Id-2:
- tx6293f1e3063a45da8691f-00620a3736
X-Amz-Request-Id:
- tx6293f1e3063a45da8691f-00620a3736
status: 400 Bad Request
code: 400
duration: ""