Skip to content

Commit a245923

Browse files
authored
Merge pull request crossplane-contrib#2214 from swisscom/fix/s3-bucket-logging-config-and-object-owner
fix(s3): bucket loggingConfig and objectOwnership
2 parents 85246f7 + 4f52961 commit a245923

File tree

5 files changed

+68
-150
lines changed

5 files changed

+68
-150
lines changed

pkg/clients/s3/bucket.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,11 @@ func UpdateBucketACL(ctx context.Context, client BucketClient, bucket *v1beta1.B
232232

233233
// BucketHasACLsDisabled returns true if ACLs are disabled for the bucket, i.e., if ObjectOwnership is set to BucketOwnerEnforced
234234
func BucketHasACLsDisabled(bucket *v1beta1.Bucket) bool {
235-
return s3types.ObjectOwnership(aws.ToString(bucket.Spec.ForProvider.ObjectOwnership)) == s3types.ObjectOwnershipBucketOwnerEnforced
235+
if s3types.ObjectOwnership(aws.ToString(bucket.Spec.ForProvider.ObjectOwnership)) == s3types.ObjectOwnershipBucketOwnerEnforced ||
236+
bucket.Spec.ForProvider.ObjectOwnership == nil {
237+
return true
238+
}
239+
return false
236240
}
237241

238242
// UpdateBucketOwnershipControls creates the OwnershipContolsInput, sends the request to put an ObjectOwnership based on the bucket

pkg/controller/s3/bucket/bucket_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,11 @@ func TestObserve(t *testing.T) {
127127
s3: s3Testing.Client(s3Testing.WithPutACL(func(ctx context.Context, input *awss3.PutBucketAclInput, opts []func(*awss3.Options)) (*awss3.PutBucketAclOutput, error) {
128128
return nil, errBoom
129129
})),
130-
cr: s3Testing.Bucket(),
130+
cr: s3Testing.Bucket(s3Testing.WithObjectOwnership("BucketOwnerPreferred")),
131131
},
132132
want: want{
133133
cr: s3Testing.Bucket(
134+
s3Testing.WithObjectOwnership("BucketOwnerPreferred"),
134135
s3Testing.WithArn(fmt.Sprintf("arn:aws:s3:::%s", s3Testing.BucketName)),
135136
),
136137
err: errBoom,

pkg/controller/s3/bucket/loggingConfig.go

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,14 @@ func (in *LoggingConfigurationClient) Observe(ctx context.Context, bucket *v1bet
5555
return NeedsUpdate, errorutils.Wrap(err, loggingGetFailed)
5656
}
5757
if !cmp.Equal(GenerateAWSLogging(bucket.Spec.ForProvider.LoggingConfiguration), external.LoggingEnabled,
58-
cmpopts.IgnoreTypes(&xpv1.Reference{}, &xpv1.Selector{}), cmpopts.IgnoreTypes(document.NoSerde{})) {
58+
cmpopts.EquateEmpty(), cmpopts.IgnoreTypes(&xpv1.Reference{}, &xpv1.Selector{}), cmpopts.IgnoreTypes(document.NoSerde{})) {
5959
return NeedsUpdate, nil
6060
}
6161
return Updated, nil
6262
}
6363

6464
// CreateOrUpdate sends a request to have resource created on AWS
6565
func (in *LoggingConfigurationClient) CreateOrUpdate(ctx context.Context, bucket *v1beta1.Bucket) error {
66-
if bucket.Spec.ForProvider.LoggingConfiguration == nil {
67-
return nil
68-
}
6966
input := GeneratePutBucketLoggingInput(meta.GetExternalName(bucket), bucket.Spec.ForProvider.LoggingConfiguration)
7067
_, err := in.client.PutBucketLogging(ctx, input)
7168
return errorutils.Wrap(err, loggingPutFailed)
@@ -76,44 +73,9 @@ func (*LoggingConfigurationClient) Delete(_ context.Context, _ *v1beta1.Bucket)
7673
return nil
7774
}
7875

79-
// LateInitialize is responsible for initializing the resource based on the external value
76+
// LateInitialize is not needed because loggingConfiguration is not something which is created be default
77+
// it means if it is not set in the desired state, but it exists on aws side it should be deleted(by CreateOrUpdate), not late initialized
8078
func (in *LoggingConfigurationClient) LateInitialize(ctx context.Context, bucket *v1beta1.Bucket) error {
81-
external, err := in.client.GetBucketLogging(ctx, &awss3.GetBucketLoggingInput{Bucket: pointer.ToOrNilIfZeroValue(meta.GetExternalName(bucket))})
82-
if err != nil {
83-
return errorutils.Wrap(err, loggingGetFailed)
84-
}
85-
86-
if external == nil || external.LoggingEnabled == nil {
87-
// There is no value send by AWS to initialize
88-
return nil
89-
}
90-
91-
if bucket.Spec.ForProvider.LoggingConfiguration == nil {
92-
// We need the configuration to exist so we can initialize
93-
bucket.Spec.ForProvider.LoggingConfiguration = &v1beta1.LoggingConfiguration{}
94-
}
95-
96-
config := bucket.Spec.ForProvider.LoggingConfiguration
97-
// Late initialize the target Bucket and target prefix
98-
config.TargetBucket = pointer.LateInitialize(config.TargetBucket, external.LoggingEnabled.TargetBucket)
99-
config.TargetPrefix = pointer.LateInitializeValueFromPtr(config.TargetPrefix, external.LoggingEnabled.TargetPrefix)
100-
// If the there is an external target grant list, and the local one does not exist
101-
// we create the target grant list
102-
if len(external.LoggingEnabled.TargetGrants) != 0 && config.TargetGrants == nil {
103-
config.TargetGrants = make([]v1beta1.TargetGrant, len(external.LoggingEnabled.TargetGrants))
104-
for i, v := range external.LoggingEnabled.TargetGrants {
105-
config.TargetGrants[i] = v1beta1.TargetGrant{
106-
Grantee: v1beta1.TargetGrantee{
107-
DisplayName: v.Grantee.DisplayName,
108-
EmailAddress: v.Grantee.EmailAddress,
109-
ID: v.Grantee.ID,
110-
Type: string(v.Grantee.Type),
111-
URI: v.Grantee.URI,
112-
},
113-
Permission: string(v.Permission),
114-
}
115-
}
116-
}
11779
return nil
11880
}
11981

@@ -125,24 +87,30 @@ func (in *LoggingConfigurationClient) SubresourceExists(bucket *v1beta1.Bucket)
12587
// GeneratePutBucketLoggingInput creates the input for the PutBucketLogging request for the S3 Client
12688
func GeneratePutBucketLoggingInput(name string, config *v1beta1.LoggingConfiguration) *awss3.PutBucketLoggingInput {
12789
bci := &awss3.PutBucketLoggingInput{
128-
Bucket: pointer.ToOrNilIfZeroValue(name),
129-
BucketLoggingStatus: &types.BucketLoggingStatus{LoggingEnabled: &types.LoggingEnabled{
130-
TargetBucket: config.TargetBucket,
131-
TargetGrants: make([]types.TargetGrant, 0),
132-
TargetPrefix: pointer.ToOrNilIfZeroValue(config.TargetPrefix),
133-
}},
90+
Bucket: pointer.ToOrNilIfZeroValue(name),
91+
BucketLoggingStatus: &types.BucketLoggingStatus{},
13492
}
135-
for _, grant := range config.TargetGrants {
136-
bci.BucketLoggingStatus.LoggingEnabled.TargetGrants = append(bci.BucketLoggingStatus.LoggingEnabled.TargetGrants, types.TargetGrant{
137-
Grantee: &types.Grantee{
138-
DisplayName: grant.Grantee.DisplayName,
139-
EmailAddress: grant.Grantee.EmailAddress,
140-
ID: grant.Grantee.ID,
141-
Type: types.Type(grant.Grantee.Type),
142-
URI: grant.Grantee.URI,
143-
},
144-
Permission: types.BucketLogsPermission(grant.Permission),
145-
})
93+
if config != nil {
94+
bci = &awss3.PutBucketLoggingInput{
95+
Bucket: pointer.ToOrNilIfZeroValue(name),
96+
BucketLoggingStatus: &types.BucketLoggingStatus{LoggingEnabled: &types.LoggingEnabled{
97+
TargetBucket: config.TargetBucket,
98+
TargetGrants: make([]types.TargetGrant, 0),
99+
TargetPrefix: pointer.ToOrNilIfZeroValue(config.TargetPrefix),
100+
}},
101+
}
102+
for _, grant := range config.TargetGrants {
103+
bci.BucketLoggingStatus.LoggingEnabled.TargetGrants = append(bci.BucketLoggingStatus.LoggingEnabled.TargetGrants, types.TargetGrant{
104+
Grantee: &types.Grantee{
105+
DisplayName: grant.Grantee.DisplayName,
106+
EmailAddress: grant.Grantee.EmailAddress,
107+
ID: grant.Grantee.ID,
108+
Type: types.Type(grant.Grantee.Type),
109+
URI: grant.Grantee.URI,
110+
},
111+
Permission: types.BucketLogsPermission(grant.Permission),
112+
})
113+
}
146114
}
147115
return bci
148116
}
@@ -155,9 +123,7 @@ func GenerateAWSLogging(local *v1beta1.LoggingConfiguration) *types.LoggingEnabl
155123
output := types.LoggingEnabled{
156124
TargetBucket: local.TargetBucket,
157125
TargetPrefix: pointer.ToOrNilIfZeroValue(local.TargetPrefix),
158-
}
159-
if local.TargetGrants != nil {
160-
output.TargetGrants = make([]types.TargetGrant, len(local.TargetGrants))
126+
TargetGrants: []types.TargetGrant{},
161127
}
162128
for i := range local.TargetGrants {
163129
target := types.TargetGrant{
@@ -171,7 +137,7 @@ func GenerateAWSLogging(local *v1beta1.LoggingConfiguration) *types.LoggingEnabl
171137
Permission: types.BucketLogsPermission(local.TargetGrants[i].Permission),
172138
}
173139

174-
output.TargetGrants[i] = target
140+
output.TargetGrants = append(output.TargetGrants, target)
175141
}
176142
return &output
177143
}

pkg/controller/s3/bucket/loggingConfig_test.go

Lines changed: 29 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ func generateLoggingConfig() *v1beta1.LoggingConfiguration {
5858
}
5959
}
6060

61+
func generateLoggingConfigNoGrants() *v1beta1.LoggingConfiguration {
62+
return &v1beta1.LoggingConfiguration{
63+
TargetBucket: &bucketName,
64+
TargetPrefix: prefix,
65+
}
66+
}
67+
6168
func generateAWSLogging() *s3types.LoggingEnabled {
6269
return &s3types.LoggingEnabled{
6370
TargetBucket: &bucketName,
@@ -75,6 +82,14 @@ func generateAWSLogging() *s3types.LoggingEnabled {
7582
}
7683
}
7784

85+
func generateAWSLoggingNoGrants() *s3types.LoggingEnabled {
86+
return &s3types.LoggingEnabled{
87+
TargetBucket: &bucketName,
88+
TargetGrants: []s3types.TargetGrant{},
89+
TargetPrefix: &prefix,
90+
}
91+
}
92+
7893
func TestLoggingObserve(t *testing.T) {
7994
type args struct {
8095
cl *LoggingConfigurationClient
@@ -146,6 +161,20 @@ func TestLoggingObserve(t *testing.T) {
146161
err: nil,
147162
},
148163
},
164+
"NoUpdateExistsWithoutGrants": {
165+
args: args{
166+
b: s3testing.Bucket(s3testing.WithLoggingConfig(generateLoggingConfigNoGrants())),
167+
cl: NewLoggingConfigurationClient(fake.MockBucketClient{
168+
MockGetBucketLogging: func(ctx context.Context, input *s3.GetBucketLoggingInput, opts []func(*s3.Options)) (*s3.GetBucketLoggingOutput, error) {
169+
return &s3.GetBucketLoggingOutput{LoggingEnabled: generateAWSLoggingNoGrants()}, nil
170+
},
171+
}),
172+
},
173+
want: want{
174+
status: Updated,
175+
err: nil,
176+
},
177+
},
149178
}
150179

151180
for name, tc := range cases {
@@ -225,89 +254,3 @@ func TestLoggingCreateOrUpdate(t *testing.T) {
225254
})
226255
}
227256
}
228-
229-
func TestLoggingLateInit(t *testing.T) {
230-
type args struct {
231-
cl SubresourceClient
232-
b *v1beta1.Bucket
233-
}
234-
235-
type want struct {
236-
err error
237-
cr *v1beta1.Bucket
238-
}
239-
240-
cases := map[string]struct {
241-
args
242-
want
243-
}{
244-
"Error": {
245-
args: args{
246-
b: s3testing.Bucket(),
247-
cl: NewLoggingConfigurationClient(fake.MockBucketClient{
248-
MockGetBucketLogging: func(ctx context.Context, input *s3.GetBucketLoggingInput, opts []func(*s3.Options)) (*s3.GetBucketLoggingOutput, error) {
249-
return &s3.GetBucketLoggingOutput{}, errBoom
250-
},
251-
}),
252-
},
253-
want: want{
254-
err: errorutils.Wrap(errBoom, loggingGetFailed),
255-
cr: s3testing.Bucket(),
256-
},
257-
},
258-
"NoLateInitEmpty": {
259-
args: args{
260-
b: s3testing.Bucket(),
261-
cl: NewLoggingConfigurationClient(fake.MockBucketClient{
262-
MockGetBucketLogging: func(ctx context.Context, input *s3.GetBucketLoggingInput, opts []func(*s3.Options)) (*s3.GetBucketLoggingOutput, error) {
263-
return &s3.GetBucketLoggingOutput{}, nil
264-
},
265-
}),
266-
},
267-
want: want{
268-
err: nil,
269-
cr: s3testing.Bucket(),
270-
},
271-
},
272-
"SuccessfulLateInit": {
273-
args: args{
274-
b: s3testing.Bucket(s3testing.WithLoggingConfig(nil)),
275-
cl: NewLoggingConfigurationClient(fake.MockBucketClient{
276-
MockGetBucketLogging: func(ctx context.Context, input *s3.GetBucketLoggingInput, opts []func(*s3.Options)) (*s3.GetBucketLoggingOutput, error) {
277-
return &s3.GetBucketLoggingOutput{LoggingEnabled: generateAWSLogging()}, nil
278-
},
279-
}),
280-
},
281-
want: want{
282-
err: nil,
283-
cr: s3testing.Bucket(s3testing.WithLoggingConfig(generateLoggingConfig())),
284-
},
285-
},
286-
"NoOpLateInit": {
287-
args: args{
288-
b: s3testing.Bucket(s3testing.WithLoggingConfig(generateLoggingConfig())),
289-
cl: NewLoggingConfigurationClient(fake.MockBucketClient{
290-
MockGetBucketLogging: func(ctx context.Context, input *s3.GetBucketLoggingInput, opts []func(*s3.Options)) (*s3.GetBucketLoggingOutput, error) {
291-
return &s3.GetBucketLoggingOutput{LoggingEnabled: &s3types.LoggingEnabled{}}, nil
292-
},
293-
}),
294-
},
295-
want: want{
296-
err: nil,
297-
cr: s3testing.Bucket(s3testing.WithLoggingConfig(generateLoggingConfig())),
298-
},
299-
},
300-
}
301-
302-
for name, tc := range cases {
303-
t.Run(name, func(t *testing.T) {
304-
err := tc.args.cl.LateInitialize(context.Background(), tc.args.b)
305-
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
306-
t.Errorf("r: -want, +got:\n%s", diff)
307-
}
308-
if diff := cmp.Diff(tc.want.cr, tc.args.b, test.EquateConditions()); diff != "" {
309-
t.Errorf("r: -want, +got:\n%s", diff)
310-
}
311-
})
312-
}
313-
}

pkg/controller/s3/testing/testHelper.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ var (
4242
// BucketModifier is a function which modifies the Bucket for testing
4343
type BucketModifier func(bucket *v1beta1.Bucket)
4444

45+
func WithObjectOwnership(s string) BucketModifier {
46+
return func(r *v1beta1.Bucket) { r.Spec.ForProvider.ObjectOwnership = &s }
47+
}
48+
4549
// WithArn sets the ARN for an S3 Bucket
4650
func WithArn(arn string) BucketModifier {
4751
return func(bucket *v1beta1.Bucket) {

0 commit comments

Comments
 (0)