github.com/ironcore-dev/gardener-extension-provider-ironcore@v0.3.2-0.20240314231816-8336447fb9a0/pkg/controller/backupentry/backupentry.go (about) 1 // SPDX-FileCopyrightText: 2023 SAP SE or an SAP affiliate company and IronCore contributors 2 // SPDX-License-Identifier: Apache-2.0 3 4 package backupentry 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/aws/credentials" 13 "github.com/aws/aws-sdk-go/aws/session" 14 "github.com/aws/aws-sdk-go/service/s3" 15 corev1 "k8s.io/api/core/v1" 16 17 "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/ironcore" 18 ) 19 20 //go:generate $MOCKGEN -copyright_file ../../../hack/license-header.txt -package backupentry -destination=mock_backupentry.go -source backupentry.go S3ClientGetter,S3ObjectLister 21 22 type s3ObjectLister interface { 23 ListObjectsPages(ctx aws.Context, s3Client *s3.S3, input *s3.ListObjectsInput, bucketName string) error 24 } 25 26 type s3ObjectListerImpl struct{} 27 28 var objectLister s3ObjectLister = s3ObjectListerImpl{} 29 30 func (o s3ObjectListerImpl) ListObjectsPages(ctx aws.Context, s3Client *s3.S3, input *s3.ListObjectsInput, bucketName string) error { 31 var delErr error 32 if err := s3Client.ListObjectsPagesWithContext(ctx, input, func(page *s3.ListObjectsOutput, lastPage bool) bool { 33 objectIDs := make([]*s3.ObjectIdentifier, 0) 34 for _, key := range page.Contents { 35 obj := &s3.ObjectIdentifier{ 36 Key: key.Key, 37 } 38 objectIDs = append(objectIDs, obj) 39 } 40 41 if len(objectIDs) != 0 { 42 if _, delErr = s3Client.DeleteObjectsWithContext(ctx, &s3.DeleteObjectsInput{ 43 Bucket: aws.String(bucketName), 44 Delete: &s3.Delete{ 45 Objects: objectIDs, 46 Quiet: aws.Bool(true), 47 }, 48 }); delErr != nil { 49 return false 50 } 51 } 52 return !lastPage 53 }); err != nil { 54 return fmt.Errorf("error listing objects pages from bucket %s: %w", bucketName, err) 55 } 56 57 if delErr != nil { 58 if aerr, ok := delErr.(awserr.Error); ok && aerr.Code() == s3.ErrCodeNoSuchKey { 59 return nil 60 } 61 return delErr 62 } 63 return nil 64 } 65 66 // DeleteObjectsWithPrefix deletes the s3 objects with the specific <prefix> 67 // from <bucket>. If it does not exist, no error is returned. 68 func DeleteObjectsWithPrefix(ctx context.Context, s3Client *s3.S3, region, bucketName, prefix string) error { 69 in := &s3.ListObjectsInput{ 70 Bucket: aws.String(bucketName), 71 Prefix: aws.String(prefix), 72 } 73 74 if err := objectLister.ListObjectsPages(ctx, s3Client, in, bucketName); err != nil { 75 return fmt.Errorf("failed to list objects pages: %w", err) 76 } 77 78 return nil 79 } 80 81 // GetS3ClientFromS3ClientSecret creates s3Client from bucket access key ID 82 // and secret access key. 83 func GetS3ClientFromS3ClientSecret(secret *corev1.Secret) (*s3.S3, error) { 84 if secret.Data == nil { 85 return nil, fmt.Errorf("secret does not contain any data") 86 } 87 88 accessKeyID, ok := secret.Data[ironcore.AccessKeyID] 89 if !ok { 90 return nil, fmt.Errorf("missing %q field in secret", ironcore.AccessKeyID) 91 } 92 93 secretAccessKey, ok := secret.Data[ironcore.SecretAccessKey] 94 if !ok { 95 return nil, fmt.Errorf("missing %q field in secret", ironcore.SecretAccessKey) 96 } 97 98 endpoint, ok := secret.Data[ironcore.Endpoint] 99 if !ok { 100 return nil, fmt.Errorf("missing %q field in secret", ironcore.Endpoint) 101 } 102 103 endpointStr := string(endpoint) 104 awsConfig := &aws.Config{ 105 Credentials: credentials.NewStaticCredentials(string(accessKeyID), string(secretAccessKey), ""), 106 Endpoint: &endpointStr, 107 } 108 109 s, err := session.NewSession(awsConfig) 110 if err != nil { 111 return nil, fmt.Errorf("failed to create session: %w", err) 112 } 113 config := &aws.Config{Region: aws.String("region")} //TODO: hardcoded the region for now, consider making it configurable if necessary 114 s3Client := s3.New(s, config) 115 116 return s3Client, nil 117 }