From 7fdc1ee43c755dec9ec825de3923eccdcfb1330d Mon Sep 17 00:00:00 2001 From: kkb0318 Date: Sat, 13 Apr 2024 19:30:57 +0900 Subject: [PATCH] add Delete Storage --- internal/client/aws.go | 66 ++++++++++++++++--- internal/controller/irsasetup_controller.go | 4 +- .../controller/irsasetup_controller_test.go | 13 +++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/internal/client/aws.go b/internal/client/aws.go index e013a53..ec6317e 100644 --- a/internal/client/aws.go +++ b/internal/client/aws.go @@ -3,7 +3,9 @@ package client import ( "bytes" "context" + "errors" "fmt" + "log" "strings" "github.com/aws/aws-sdk-go-v2/aws" @@ -11,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/aws/smithy-go" ) type AwsClientFactory struct { @@ -26,6 +29,8 @@ type AwsS3API interface { PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) DeletePublicAccessBlock(ctx context.Context, params *s3.DeletePublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.DeletePublicAccessBlockOutput, error) PutBucketOwnershipControls(ctx context.Context, params *s3.PutBucketOwnershipControlsInput, optFns ...func(*s3.Options)) (*s3.PutBucketOwnershipControlsOutput, error) + DeleteBucket(ctx context.Context, params *s3.DeleteBucketInput, optFns ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) + DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error) } type AwsClient interface { @@ -58,13 +63,15 @@ func (a *AwsClientFactory) S3Client(bucketName, region string) *AwsS3Client { } type AwsS3Client struct { - AwsS3API + Client AwsS3API region string bucketName string } +// PutObjectPublic uploads a file to an S3 bucket and sets its access level to public read. +// This means the file can be read by anyone on the internet. func (a *AwsS3Client) PutObjectPublic(ctx context.Context, key string, body []byte) error { - _, err := a.PutObject(ctx, &s3.PutObjectInput{ + _, err := a.Client.PutObject(ctx, &s3.PutObjectInput{ Bucket: aws.String(a.bucketName), Key: aws.String(key), ACL: types.ObjectCannedACLPublicRead, @@ -74,22 +81,29 @@ func (a *AwsS3Client) PutObjectPublic(ctx context.Context, key string, body []by return err } +// CreateBucketPublic creates a new S3 bucket with public access settings in the specified region. +// The function configures the bucket to have its ownership controlled by the bucket creator. func (a *AwsS3Client) CreateBucketPublic(ctx context.Context) error { bucket := aws.String(a.bucketName) - _, err := a.CreateBucket(ctx, &s3.CreateBucketInput{ + _, err := a.Client.CreateBucket(ctx, &s3.CreateBucketInput{ Bucket: bucket, CreateBucketConfiguration: &types.CreateBucketConfiguration{ LocationConstraint: types.BucketLocationConstraint(a.Region()), }, }) if err != nil { - return err + var bucketAlreadyOwnedByYou *types.BucketAlreadyOwnedByYou + if errors.As(err, &bucketAlreadyOwnedByYou) { + log.Println("skipped error", err) + } else { + return err + } } - _, err = a.DeletePublicAccessBlock(ctx, &s3.DeletePublicAccessBlockInput{Bucket: bucket}) + _, err = a.Client.DeletePublicAccessBlock(ctx, &s3.DeletePublicAccessBlockInput{Bucket: bucket}) if err != nil { return err } - _, err = a.PutBucketOwnershipControls(ctx, &s3.PutBucketOwnershipControlsInput{ + _, err = a.Client.PutBucketOwnershipControls(ctx, &s3.PutBucketOwnershipControlsInput{ Bucket: bucket, OwnershipControls: &types.OwnershipControls{ Rules: []types.OwnershipControlsRule{ @@ -105,6 +119,41 @@ func (a *AwsS3Client) CreateBucketPublic(ctx context.Context) error { return nil } +// DeleteBucket attempts to delete the specified bucket. +// If the bucket contains any objects, the deletion will not be forced to prevent accidental data loss. +func (a *AwsS3Client) DeleteBucket(ctx context.Context) error { + _, err := a.Client.DeleteBucket(ctx, &s3.DeleteBucketInput{ + Bucket: aws.String(a.bucketName), + }) + if err != nil { + var ae smithy.APIError + if errors.As(err, &ae) && ae.ErrorCode() == "BucketNotEmpty" { + log.Println("skipped error", err) + } else { + return err + } + } + return nil +} + +// DeleteObjects removes a list of objects from a specified bucket. +func (a *AwsS3Client) DeleteObjects(ctx context.Context, objectKeys []string) error { + var objectIds []types.ObjectIdentifier + for _, key := range objectKeys { + objectIds = append(objectIds, types.ObjectIdentifier{Key: aws.String(key)}) + } + output, err := a.Client.DeleteObjects(ctx, &s3.DeleteObjectsInput{ + Bucket: aws.String(a.bucketName), + Delete: &types.Delete{Objects: objectIds}, + }) + if err != nil { + return err + } else { + log.Printf("Deleted %v objects.\n", len(output.Deleted)) + } + return err +} + func (a *AwsS3Client) BucketName() string { return a.bucketName } @@ -114,11 +163,12 @@ func (a *AwsS3Client) Region() string { } type AwsIamClient struct { - AwsIamAPI + Client AwsIamAPI } +// CreateOIDCProvider creates an OpenID Connect (OIDC) provider in AWS IAM. func (a *AwsIamClient) CreateOIDCProvider(ctx context.Context, providerUrl string) (string, error) { - result, err := a.CreateOpenIDConnectProvider(ctx, &iam.CreateOpenIDConnectProviderInput{ + result, err := a.Client.CreateOpenIDConnectProvider(ctx, &iam.CreateOpenIDConnectProviderInput{ Url: &providerUrl, ClientIDList: []string{"sts.amazonaws.com"}, ThumbprintList: []string{ diff --git a/internal/controller/irsasetup_controller.go b/internal/controller/irsasetup_controller.go index 8bc0e3c..08b4ab5 100644 --- a/internal/controller/irsasetup_controller.go +++ b/internal/controller/irsasetup_controller.go @@ -102,7 +102,7 @@ func (r *IRSASetupReconciler) reconcileDelete(ctx context.Context, obj *irsav1al if err != nil { return err } - secret, err := manifests.NewSecretBuilder().Build("name", "default") + secret, err := manifests.NewSecretBuilder().Build("name", "default") // TODO: if err != nil { return err } @@ -132,7 +132,7 @@ func reconcileSelfhosted(ctx context.Context, obj *irsav1alpha1.IRSASetup, awsCl if err != nil { return err } - secret, err := manifests.NewSecretBuilder().WithSSHKey(*keyPair).Build("name", "default") + secret, err := manifests.NewSecretBuilder().WithSSHKey(*keyPair).Build("name", "default") // TODO: if err != nil { return err } diff --git a/internal/controller/irsasetup_controller_test.go b/internal/controller/irsasetup_controller_test.go index 915ac5b..fbd7521 100644 --- a/internal/controller/irsasetup_controller_test.go +++ b/internal/controller/irsasetup_controller_test.go @@ -75,6 +75,7 @@ var _ = Describe("IRSASetup Controller", func() { It("should successfully reconcile the resource", func() { awsClient := newMockAwsClient() expected := []types.NamespacedName{ + // TODO: {Name: "name", Namespace: "default"}, } @@ -118,11 +119,11 @@ func newMockAwsClient() awsclient.AwsClient { type mockAwsClient struct{} func (m *mockAwsClient) IamClient() *awsclient.AwsIamClient { - return &awsclient.AwsIamClient{AwsIamAPI: &mockAwsIamAPI{}} + return &awsclient.AwsIamClient{Client: &mockAwsIamAPI{}} } func (m *mockAwsClient) S3Client(region, bucketName string) *awsclient.AwsS3Client { - return &awsclient.AwsS3Client{AwsS3API: &mockAwsS3API{}} + return &awsclient.AwsS3Client{Client: &mockAwsS3API{}} } type ( @@ -149,3 +150,11 @@ func (m *mockAwsS3API) DeletePublicAccessBlock(ctx context.Context, params *s3.D func (m *mockAwsS3API) PutBucketOwnershipControls(ctx context.Context, params *s3.PutBucketOwnershipControlsInput, optFns ...func(*s3.Options)) (*s3.PutBucketOwnershipControlsOutput, error) { return nil, nil } + +func (m *mockAwsS3API) DeleteBucket(ctx context.Context, params *s3.DeleteBucketInput, optFns ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) { + return nil, nil +} + +func (m *mockAwsS3API) DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error) { + return nil, nil +}