Skip to content

Commit 89b7e3d

Browse files
committed
aws cloudstorage bucket refactor
1 parent fbab17b commit 89b7e3d

File tree

10 files changed

+339
-182
lines changed

10 files changed

+339
-182
lines changed

api/v1alpha1/cloud_storage_types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ type CloudStorage struct {
1919
type CloudStorageProvider string
2020

2121
const (
22-
AWSBucketProvider CloudStorageProvider = "aws"
22+
AWSBucketProvider CloudStorageProvider = "aws"
23+
AzureBucketProvider CloudStorageProvider = "azure"
24+
GCPBucketProvider CloudStorageProvider = "gcp"
2325
)
2426

2527
type CloudStorageSpec struct {
26-
// Name is the name requested for the bucket
28+
// Name is the name requested for the bucket (aws) or container (azure)
2729
Name string `json:"name"`
2830
// CreationSecret is the secret that is needed to be used while creating the bucket.
2931
CreationSecret corev1.SecretKeySelector `json:"creationSecret"`

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/oadp.openshift.io_cloudstorages.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ spec:
5757
for AWS Buckets
5858
type: boolean
5959
name:
60-
description: Name is the name requested for the bucket
60+
description: Name is the name requested for the bucket (aws) or container
61+
(azure)
6162
type: string
6263
provider:
6364
enum:

controllers/bsl.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,27 @@ func (r *DPAReconciler) ReconcileBackupStorageLocations(log logr.Logger) (bool,
134134
}
135135
bsl.Spec.Credential = bslSpec.CloudStorage.Credential
136136
bsl.Spec.Default = bslSpec.CloudStorage.Default
137-
bsl.Spec.Provider = AWSProvider
138137
bsl.Spec.ObjectStorage = &velerov1.ObjectStorageLocation{
139138
Bucket: bucket.Spec.Name,
140139
}
140+
switch bucket.Spec.Provider {
141+
case oadpv1alpha1.AWSBucketProvider:
142+
{
143+
bsl.Spec.Provider = AWSProvider
144+
}
145+
case oadpv1alpha1.AzureBucketProvider:
146+
{
147+
panic("Azure provider not yet supported")
148+
}
149+
case oadpv1alpha1.GCPBucketProvider:
150+
{
151+
panic("GCP provider not yet supported")
152+
}
153+
default:
154+
{
155+
return fmt.Errorf("invalid provider")
156+
}
157+
}
141158
}
142159
return nil
143160
})

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ module github.com/openshift/oadp-operator
33
go 1.16
44

55
require (
6+
github.com/Azure-Samples/azure-sdk-for-go-samples v0.0.0-20210506191746-b49c4162aa1d
7+
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.20.0 // indirect
8+
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.2.0 // indirect
9+
github.com/Azure/azure-storage-blob-go v0.0.0-20181023070848-cf01652132cc
610
github.com/aws/aws-sdk-go v1.28.2
711
github.com/go-logr/logr v0.4.0
812
github.com/google/uuid v1.1.2

go.sum

Lines changed: 102 additions & 0 deletions
Large diffs are not rendered by default.

pkg/bucket/aws.go

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package bucket
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/aws/aws-sdk-go/aws"
7+
"github.com/aws/aws-sdk-go/aws/awserr"
8+
"github.com/aws/aws-sdk-go/aws/session"
9+
"github.com/aws/aws-sdk-go/service/s3"
10+
"github.com/aws/aws-sdk-go/service/s3/s3iface"
11+
"github.com/openshift/oadp-operator/api/v1alpha1"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
)
14+
15+
type awsConfigMap map[awsConfigKey]string
16+
type awsConfigKey string
17+
18+
// Config Keys for AWS
19+
const (
20+
region awsConfigKey = "region"
21+
s3ForcePathStyle awsConfigKey = "s3ForcePathStyle"
22+
s3Url awsConfigKey = "s3Url"
23+
publicUrl awsConfigKey = "publicUrl"
24+
serverSideEncryption awsConfigKey = "serverSideEncryption"
25+
kmsKeyID awsConfigKey = "kmsKeyID"
26+
signatureVersion awsConfigKey = "signatureVersion"
27+
profile awsConfigKey = "profile"
28+
insecureSkipTLSVerify awsConfigKey = "insecureSkipTLSVerify"
29+
)
30+
31+
type awsBucketClient struct {
32+
bucket v1alpha1.CloudStorage
33+
client client.Client
34+
}
35+
36+
func (a awsBucketClient) Exists() (bool, error) {
37+
s3Client, err := a.getS3Client()
38+
if err != nil {
39+
return false, err
40+
}
41+
input := &s3.HeadBucketInput{
42+
Bucket: aws.String(a.bucket.Spec.Name),
43+
}
44+
_, err = s3Client.HeadBucket(input)
45+
if err != nil {
46+
if aerr, ok := err.(awserr.Error); ok {
47+
switch aerr.Code() {
48+
// This is supposed to say "NoSuchBucket", but actually emits "NotFound"
49+
// https://github.com/aws/aws-sdk-go/issues/2593
50+
case s3.ErrCodeNoSuchBucket, "NotFound":
51+
return false, nil
52+
default:
53+
// Return true, because we are unable to detemine if bucket exists or not
54+
return true, fmt.Errorf("unable to determine bucket %v status: %v", a.bucket.Spec.Name, aerr.Error())
55+
}
56+
} else {
57+
// Return true, because we are unable to detemine if bucket exists or not
58+
return true, fmt.Errorf("unable to determine bucket %v status: %v", a.bucket.Spec.Name, aerr.Error())
59+
}
60+
}
61+
62+
err = a.tagBucket()
63+
if err != nil {
64+
return true, err
65+
}
66+
67+
return true, nil
68+
}
69+
70+
func (a awsBucketClient) Create() (bool, error) {
71+
s3Client, err := a.getS3Client()
72+
if err != nil {
73+
return false, err
74+
}
75+
createBucketInput := &s3.CreateBucketInput{
76+
ACL: aws.String(s3.BucketCannedACLPrivate),
77+
Bucket: aws.String(a.bucket.Spec.Name),
78+
}
79+
if a.bucket.Spec.Region != "us-east-1" {
80+
createBucketConfiguration := &s3.CreateBucketConfiguration{
81+
LocationConstraint: &a.bucket.Spec.Region,
82+
}
83+
createBucketInput.SetCreateBucketConfiguration(createBucketConfiguration)
84+
}
85+
if err := createBucketInput.Validate(); err != nil {
86+
return false, fmt.Errorf("unable to validate %v bucket creation configuration: %v", a.bucket.Spec.Name, err)
87+
}
88+
89+
_, err = s3Client.CreateBucket(createBucketInput)
90+
if err != nil {
91+
return false, err
92+
}
93+
94+
// tag Bucket.
95+
err = a.tagBucket()
96+
if err != nil {
97+
return true, err
98+
}
99+
100+
return true, nil
101+
}
102+
103+
func (a awsBucketClient) tagBucket() error {
104+
s3Client, err := a.getS3Client()
105+
// Clear bucket tags.
106+
if err != nil {
107+
return err
108+
}
109+
deleteInput := &s3.DeleteBucketTaggingInput{Bucket: aws.String(a.bucket.Spec.Name)}
110+
_, err = s3Client.DeleteBucketTagging(deleteInput)
111+
if err != nil {
112+
return err
113+
}
114+
input := CreateBucketTaggingInput(a.bucket.Spec.Name, a.bucket.Spec.Tags)
115+
116+
_, err = s3Client.PutBucketTagging(input)
117+
if err != nil {
118+
return err
119+
}
120+
return nil
121+
}
122+
123+
// CreateBucketTaggingInput creates an S3 PutBucketTaggingInput object,
124+
// which is used to associate a list of tags with a bucket.
125+
func CreateBucketTaggingInput(bucketname string, tags map[string]string) *s3.PutBucketTaggingInput {
126+
putInput := &s3.PutBucketTaggingInput{
127+
Bucket: aws.String(bucketname),
128+
Tagging: &s3.Tagging{
129+
TagSet: []*s3.Tag{},
130+
},
131+
}
132+
for key, value := range tags {
133+
newTag := s3.Tag{
134+
Key: aws.String(key),
135+
Value: aws.String(value),
136+
}
137+
putInput.Tagging.TagSet = append(putInput.Tagging.TagSet, &newTag)
138+
}
139+
return putInput
140+
}
141+
142+
func (a awsBucketClient) getS3Client() (s3iface.S3API, error) {
143+
awsConfig := &aws.Config{Region: &a.bucket.Spec.Region}
144+
cred, err := getCredentialFromSecret(a)
145+
if err != nil {
146+
return nil, err
147+
}
148+
149+
opts := session.Options{
150+
Config: *awsConfig,
151+
SharedConfigFiles: []string{cred},
152+
}
153+
154+
if a.bucket.Spec.EnableSharedConfig != nil && *a.bucket.Spec.EnableSharedConfig {
155+
opts.SharedConfigState = session.SharedConfigEnable
156+
}
157+
158+
s, err := session.NewSessionWithOptions(opts)
159+
if err != nil {
160+
return nil, err
161+
}
162+
return s3.New(s), nil
163+
}
164+
165+
func (a awsBucketClient) ForceCredentialRefresh() error {
166+
return nil
167+
}
168+
169+
func (a awsBucketClient) Delete() (bool, error) {
170+
s3Client, err := a.getS3Client()
171+
if err != nil {
172+
return false, err
173+
}
174+
deleteBucketInput := &s3.DeleteBucketInput{
175+
Bucket: aws.String(a.getCloudStorage().Spec.Name),
176+
}
177+
178+
if err := deleteBucketInput.Validate(); err != nil {
179+
return false, fmt.Errorf("unable to validate %v bucket deletion configuration: %v", a.getCloudStorage().Spec.Name, err)
180+
}
181+
182+
_, err = s3Client.DeleteBucket(deleteBucketInput)
183+
if err != nil {
184+
return false, err
185+
}
186+
187+
return true, nil
188+
}
189+
190+
func (a awsBucketClient) getClient() client.Client {
191+
return a.client
192+
}
193+
194+
func (a awsBucketClient) getCloudStorage() v1alpha1.CloudStorage {
195+
return a.bucket
196+
}

0 commit comments

Comments
 (0)