Skip to content

Commit

Permalink
add subresource versioning WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
nolancon committed Jun 20, 2024
1 parent 54eee21 commit 1e11adc
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 4 deletions.
4 changes: 3 additions & 1 deletion examples/sample/bucket.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ kind: Bucket
metadata:
name: test-bucket
spec:
forProvider: {}
forProvider:
versioningConfiguration:
status: Enabled
2 changes: 2 additions & 0 deletions internal/backendstore/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type S3Client interface {
PutBucketPolicy(context.Context, *s3.PutBucketPolicyInput, ...func(*s3.Options)) (*s3.PutBucketPolicyOutput, error)
GetBucketPolicy(context.Context, *s3.GetBucketPolicyInput, ...func(*s3.Options)) (*s3.GetBucketPolicyOutput, error)
DeleteBucketPolicy(context.Context, *s3.DeleteBucketPolicyInput, ...func(*s3.Options)) (*s3.DeleteBucketPolicyOutput, error)
PutBucketVersioning(context.Context, *s3.PutBucketVersioningInput, ...func(*s3.Options)) (*s3.PutBucketVersioningOutput, error)
GetBucketVersioning(context.Context, *s3.GetBucketVersioningInput, ...func(*s3.Options)) (*s3.GetBucketVersioningOutput, error)
}

//counterfeiter:generate . STSClient
Expand Down
166 changes: 166 additions & 0 deletions internal/backendstore/backendstorefakes/fake_s3client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 57 additions & 2 deletions internal/controller/bucket/bucket_backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ func (b *bucketBackends) getLifecycleConfigCondition(bucketName, backendName str
return b.backends[bucketName][backendName].LifecycleConfigurationCondition
}

func (b *bucketBackends) setVersioningConfigCondition(bucketName, backendName string, c *xpv1.Condition) {
b.mu.Lock()
defer b.mu.Unlock()

if b.backends[bucketName] == nil {
b.backends[bucketName] = make(v1alpha1.Backends)
}

if b.backends[bucketName][backendName] == nil {
b.backends[bucketName][backendName] = &v1alpha1.BackendInfo{}
}

b.backends[bucketName][backendName].VersioningConfigurationCondition = c
}

func (b *bucketBackends) getVersioningConfigCondition(bucketName, backendName string) *xpv1.Condition {
b.mu.RLock()
defer b.mu.RUnlock()

if _, ok := b.backends[bucketName]; !ok {
return nil
}

if _, ok := b.backends[bucketName][backendName]; !ok {
return nil
}

return b.backends[bucketName][backendName].VersioningConfigurationCondition
}

func (b *bucketBackends) deleteBackend(bucketName, backendName string) {
b.mu.Lock()
defer b.mu.Unlock()
Expand Down Expand Up @@ -143,7 +173,7 @@ func (b *bucketBackends) countBucketsAvailableOnBackends(bucket *v1alpha1.Bucket
return i
}

// isLifecycleConfigAvailableOnBackends checks the backends listed in Spec.Providers against
// isLifecycleConfigAvailableOnBackends checks the backends listed in providerNames against
// bucketBackends to ensure lifecycle configurations are considered Available on all desired backends.
func (b *bucketBackends) isLifecycleConfigAvailableOnBackends(bucket *v1alpha1.Bucket, providerNames []string, c map[string]backendstore.S3Client) bool {
for _, backendName := range providerNames {
Expand All @@ -168,7 +198,7 @@ func (b *bucketBackends) isLifecycleConfigAvailableOnBackends(bucket *v1alpha1.B
return true
}

// isLifecycleConfigRemovedFromBackends checks the backends listed in Spec.Providers against
// isLifecycleConfigRemovedFromBackends checks the backends listed in providerNames against
// bucketBackends to ensure lifecycle configurations are removed from all desired backends.
func (b *bucketBackends) isLifecycleConfigRemovedFromBackends(bucket *v1alpha1.Bucket, providerNames []string, c map[string]backendstore.S3Client) bool {
for _, backendName := range providerNames {
Expand All @@ -187,3 +217,28 @@ func (b *bucketBackends) isLifecycleConfigRemovedFromBackends(bucket *v1alpha1.B

return true
}

// isVersioningConfigAvailableOnBackends checks the backends listed in providerNames against
// bucketBackends to ensure versioning configurations are considered Available on all desired backends.
func (b *bucketBackends) isVersioningConfigAvailableOnBackends(bucket *v1alpha1.Bucket, providerNames []string, c map[string]backendstore.S3Client) bool {
for _, backendName := range providerNames {
if _, ok := c[backendName]; !ok {
// This backend does not exist in the list of available backends.
// The backend may be offline, so it is skipped.
continue
}

vCondition := b.getVersioningConfigCondition(bucket.Name, backendName)
if vCondition == nil {
// The versioningconfig has not been created on this backend.
return false
}

if !vCondition.Equal(xpv1.Available()) {
// The versioningconfig is not Available on this backend.
return false
}
}

return true
}
4 changes: 4 additions & 0 deletions internal/controller/bucket/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const (
errObserveLifecycleConfig = "failed to observe bucket lifecycle configuration"
errHandleLifecycleConfig = "failed to handle bucket lifecycle configuration"

// Versioning configuration error messages.
errObserveVersioningConfig = "failed to observe bucket versioning configuration"
errHandleVersioningConfig = "failed to handle bucket versioning configuration"

// ACL error messages.
errObserveAcl = "failed to observe bucket acl"
errHandleAcl = "failed to handle bucket acl"
Expand Down
6 changes: 6 additions & 0 deletions internal/controller/bucket/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func isPauseRequired(bucket *v1alpha1.Bucket, providerNames []string, minReplica
return false
}

// If versioning config is specified in the spec, we should only pause once the
// versioning config is available on all backends.
if bucket.Spec.ForProvider.VersioningConfiguration != nil && !bb.isVersioningConfigAvailableOnBackends(bucket, providerNames, c) {
return false
}

return (bucket.Spec.AutoPause || autopauseEnabled) &&
// Only return true if this label value is "".
// This is to allow the user to delete a paused bucket with autopause enabled.
Expand Down
1 change: 1 addition & 0 deletions internal/controller/bucket/subresources.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func NewSubresourceClients(b *backendstore.BackendStore, h *s3clienthandler.Hand
NewLifecycleConfigurationClient(b, h, l.WithValues("lifecycle-configuration-client", managed.ControllerName(v1alpha1.BucketGroupKind))),
NewACLClient(b, h, l.WithValues("acl-client", managed.ControllerName(v1alpha1.BucketGroupKind))),
NewPolicyClient(b, h, l.WithValues("policy-client", managed.ControllerName(v1alpha1.BucketGroupKind))),
NewVersioningConfigurationClient(b, h, l.WithValues("versioning-configuration-client", managed.ControllerName(v1alpha1.BucketGroupKind))),
}
}

Expand Down
11 changes: 10 additions & 1 deletion internal/controller/bucket/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,18 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext

// Whether buckets are updated successfully or not on backends, we need to update the
// Bucket CR Status in all cases to represent the conditions of each individual bucket.
cls := c.backendStore.GetBackendS3Clients(allBackendsToUpdateOn)
if err := c.updateBucketCR(ctx, bucket,
func(bucketDeepCopy, bucketLatest *v1alpha1.Bucket) UpdateRequired {
// If a VersioningConfiguration is specified in the Spec, but is not available on all
// backends, set the VersioningConfiguration in the Status to nil as we cannot consider
// this operation to have been a success. This value was preliminarily set by the
// VersioningConfiguration subresource client.
if bucketDeepCopy.Spec.ForProvider.VersioningConfiguration != nil &&
!bucketBackends.isVersioningConfigAvailableOnBackends(bucketDeepCopy, allBackendsToUpdateOn, cls) {
bucketLatest.Status.AtProvider.VersioningConfiguration = nil
}

setBucketStatus(bucketLatest, bucketBackends, allBackendsToUpdateOn, c.minReplicas)

return NeedsStatusUpdate
Expand All @@ -107,7 +117,6 @@ func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.Ext
// criteria is met before pausing a Bucket CR. Otherwise we check to see if there are
// backends that the bucket was not updated on and if so, we set the updateAllErr
// which will be returned at the end of this function, triggering a requeue.
cls := c.backendStore.GetBackendS3Clients(allBackendsToUpdateOn)
if isPauseRequired(bucketLatest, allBackendsToUpdateOn, c.minReplicas, cls, bucketBackends, c.autoPauseBucket) {
c.log.Info("Auto pausing bucket", consts.KeyBucketName, bucket.Name)
bucketLatest.Labels[meta.AnnotationKeyReconciliationPaused] = True
Expand Down
Loading

0 comments on commit 1e11adc

Please sign in to comment.