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  }