88 "hash/crc32"
99 "net/http"
1010 "os"
11+ "runtime"
1112 "strings"
1213 "time"
1314
@@ -18,15 +19,19 @@ import (
1819 "github.com/aws/aws-sdk-go/service/s3"
1920 "github.com/hashicorp/aws-sdk-go-base/tfawserr"
2021 awspolicy "github.com/hashicorp/awspolicyequivalence"
22+ "github.com/hashicorp/go-multierror"
2123 "github.com/hashicorp/terraform-plugin-log/tflog"
2224 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
2325 "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2426 "github.com/scaleway/scaleway-sdk-go/scw"
27+ "github.com/scaleway/terraform-provider-scaleway/v2/internal"
2528)
2629
2730const (
2831 defaultObjectBucketTimeout = 10 * time .Minute
2932 retryOnAWSAPI = 2 * time .Minute
33+
34+ maxObjectVersionDeletionWorkers = 8
3035)
3136
3237func newS3Client (httpClient * http.Client , region , accessKey , secretKey string ) (* s3.S3 , error ) {
@@ -305,58 +310,94 @@ func removeS3ObjectVersionLegalHold(conn *s3.S3, bucketName string, objectVersio
305310}
306311
307312func deleteS3ObjectVersions (ctx context.Context , conn * s3.S3 , bucketName string , force bool ) error {
308- var err error
313+ var globalErr error
309314 listInput := & s3.ListObjectVersionsInput {
310315 Bucket : scw .StringPtr (bucketName ),
311316 }
317+
318+ deletionWorkers := runtime .NumCPU ()
319+ if deletionWorkers > maxObjectVersionDeletionWorkers {
320+ deletionWorkers = maxObjectVersionDeletionWorkers
321+ }
322+
312323 listErr := conn .ListObjectVersionsPagesWithContext (ctx , listInput , func (page * s3.ListObjectVersionsOutput , lastPage bool ) bool {
324+ pool := internal .NewWorkerPool (deletionWorkers )
325+
313326 for _ , objectVersion := range page .Versions {
314- objectKey := aws .StringValue (objectVersion .Key )
315- objectVersionID := aws .StringValue (objectVersion .VersionId )
316- err = deleteS3ObjectVersion (conn , bucketName , objectKey , objectVersionID , force )
317-
318- if isS3Err (err , ErrCodeAccessDenied , "" ) && force {
319- legalHoldRemoved , errLegal := removeS3ObjectVersionLegalHold (conn , bucketName , objectVersion )
320- if errLegal != nil {
321- err = fmt .Errorf ("failed to remove legal hold: %s" , errLegal )
322- return false
327+ objectVersion := objectVersion
328+
329+ pool .AddTask (func () error {
330+ objectKey := aws .StringValue (objectVersion .Key )
331+ objectVersionID := aws .StringValue (objectVersion .VersionId )
332+ err := deleteS3ObjectVersion (conn , bucketName , objectKey , objectVersionID , force )
333+
334+ if isS3Err (err , ErrCodeAccessDenied , "" ) && force {
335+ legalHoldRemoved , errLegal := removeS3ObjectVersionLegalHold (conn , bucketName , objectVersion )
336+ if errLegal != nil {
337+ return fmt .Errorf ("failed to remove legal hold: %s" , errLegal )
338+ }
339+
340+ if legalHoldRemoved {
341+ err = deleteS3ObjectVersion (conn , bucketName , objectKey , objectVersionID , force )
342+ }
323343 }
324- if legalHoldRemoved {
325- err = deleteS3ObjectVersion (conn , bucketName , objectKey , objectVersionID , force )
344+
345+ if err != nil {
346+ return fmt .Errorf ("failed to delete S3 object: %s" , err )
326347 }
327- }
328- if err != nil {
329- err = fmt .Errorf ("failed to delete S3 object: %s" , err )
330- return false
331- }
348+
349+ return nil
350+ })
332351 }
352+
353+ errors := pool .CloseAndWait ()
354+ if len (errors ) > 0 {
355+ globalErr = multierror .Append (nil , errors ... )
356+ return false
357+ }
358+
333359 return true
334360 })
335361 if listErr != nil {
336- return fmt .Errorf ("error listing S3 objects: %s" , err )
362+ return fmt .Errorf ("error listing S3 objects: %s" , globalErr )
337363 }
338- if err != nil {
339- return err
364+ if globalErr != nil {
365+ return globalErr
340366 }
367+
341368 listErr = conn .ListObjectVersionsPagesWithContext (ctx , listInput , func (page * s3.ListObjectVersionsOutput , lastPage bool ) bool {
369+ pool := internal .NewWorkerPool (deletionWorkers )
370+
342371 for _ , deleteMarkerEntry := range page .DeleteMarkers {
343- deleteMarkerKey := aws .StringValue (deleteMarkerEntry .Key )
344- deleteMarkerVersionsID := aws .StringValue (deleteMarkerEntry .VersionId )
345- err = deleteS3ObjectVersion (conn , bucketName , deleteMarkerKey , deleteMarkerVersionsID , force )
372+ deleteMarkerEntry := deleteMarkerEntry
373+
374+ pool .AddTask (func () error {
375+ deleteMarkerKey := aws .StringValue (deleteMarkerEntry .Key )
376+ deleteMarkerVersionsID := aws .StringValue (deleteMarkerEntry .VersionId )
377+ err := deleteS3ObjectVersion (conn , bucketName , deleteMarkerKey , deleteMarkerVersionsID , force )
378+ if err != nil {
379+ return fmt .Errorf ("failed to delete S3 object delete marker: %s" , err )
380+ }
346381
347- if err != nil {
348- err = fmt .Errorf ("failed to delete S3 object delete marker: %s" , err )
349- return false
350- }
382+ return nil
383+ })
384+ }
385+
386+ errors := pool .CloseAndWait ()
387+ if len (errors ) > 0 {
388+ globalErr = multierror .Append (nil , errors ... )
389+ return false
351390 }
391+
352392 return true
353393 })
354394 if listErr != nil {
355- return fmt .Errorf ("error listing S3 objects for delete markers: %s" , err )
395+ return fmt .Errorf ("error listing S3 objects for delete markers: %s" , globalErr )
356396 }
357- if err != nil {
358- return err
397+ if globalErr != nil {
398+ return globalErr
359399 }
400+
360401 return nil
361402}
362403
0 commit comments