Skip to content

Commit

Permalink
Merge pull request #40 from raffis/feat-bucketpolicy
Browse files Browse the repository at this point in the history
feat: add bucket policy
  • Loading branch information
Kidswiss authored Apr 5, 2024
2 parents 19554f7 + 0d81c42 commit f161d9a
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

Crossplane provider for managing resources on min.io.

Documentation: https://vshn.github.io/provider-minio/
Documentation: https://vshn.github.io/provider-minio/provider-minio/

## Local Development

Expand Down
4 changes: 4 additions & 0 deletions apis/minio/v1/bucket_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ type BucketParameters struct {
// `DeleteAll` recursively deletes all objects in the bucket and then removes it.
// To skip deletion of the bucket (orphan it) set `spec.deletionPolicy=Orphan`.
BucketDeletionPolicy BucketDeletionPolicy `json:"bucketDeletionPolicy,omitempty"`

// Policy is a raw S3 bucket policy.
// Please consult https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html for more details about the policy.
Policy *string `json:"policy,omitempty"`
}

type BucketProviderStatus struct {
Expand Down
7 changes: 6 additions & 1 deletion apis/minio/v1/zz_generated.deepcopy.go

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

7 changes: 7 additions & 0 deletions operator/bucket/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ func (b *bucketClient) Create(ctx context.Context, mg resource.Managed) (managed
return managed.ExternalCreation{}, err
}

if bucket.Spec.ForProvider.Policy != nil {
err = b.mc.SetBucketPolicy(ctx, bucket.GetBucketName(), *bucket.Spec.ForProvider.Policy)
if err != nil {
return managed.ExternalCreation{}, err
}
}

b.setLock(bucket)

return managed.ExternalCreation{}, b.emitCreationEvent(bucket)
Expand Down
22 changes: 21 additions & 1 deletion operator/bucket/observe.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ var bucketExistsFn = func(ctx context.Context, mc *minio.Client, bucketName stri
return mc.BucketExists(ctx, bucketName)
}

var bucketPolicyLatestFn = func(ctx context.Context, mc *minio.Client, bucketName string, policy string) (bool, error) {
current, err := mc.GetBucketPolicy(ctx, bucketName)
if err != nil {
return false, err
}

return current == policy, nil
}

func (d *bucketClient) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) {
log := controllerruntime.LoggerFrom(ctx)
log.V(1).Info("observing resource")
Expand Down Expand Up @@ -45,7 +54,18 @@ func (d *bucketClient) Observe(ctx context.Context, mg resource.Managed) (manage
if _, hasAnnotation := bucket.GetAnnotations()[lockAnnotation]; hasAnnotation && exists {
bucket.Status.AtProvider.BucketName = bucketName
bucket.SetConditions(xpv1.Available())
return managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: true}, nil

isLatest := true
if bucket.Spec.ForProvider.Policy != nil {
u, err := bucketPolicyLatestFn(ctx, d.mc, bucketName, *bucket.Spec.ForProvider.Policy)
if err != nil {
return managed.ExternalObservation{}, errors.Wrap(err, "cannot determine whether a bucket policy exists")
}

isLatest = u
}

return managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: isLatest}, nil
} else if exists {
return managed.ExternalObservation{}, fmt.Errorf("bucket already exists, try changing bucket name: %s", bucketName)
}
Expand Down
28 changes: 28 additions & 0 deletions operator/bucket/observe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import (
)

func TestProvisioningPipeline_Observe(t *testing.T) {
policy := "policy-struct"
tests := map[string]struct {
givenBucket *miniov1.Bucket
bucketExists bool
returnError error
policyLatest bool

expectedError string
expectedResult managed.ExternalObservation
Expand All @@ -31,6 +33,13 @@ func TestProvisioningPipeline_Observe(t *testing.T) {
},
expectedResult: managed.ExternalObservation{},
},
"NewBucketWithPolicyDoesntYetExistOnMinio": {
givenBucket: &miniov1.Bucket{Spec: miniov1.BucketSpec{ForProvider: miniov1.BucketParameters{
BucketName: "my-bucket-with-policy",
Policy: &policy}},
},
expectedResult: managed.ExternalObservation{},
},
"BucketExistsAndAccessibleWithOurCredentials": {
givenBucket: &miniov1.Bucket{
ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
Expand Down Expand Up @@ -80,13 +89,32 @@ func TestProvisioningPipeline_Observe(t *testing.T) {
expectedResult: managed.ExternalObservation{},
expectedError: "mismatching endpointURL and zone, or bucket exists already in a different region, try changing bucket name: 301 Moved Permanently",
},
"BucketPolicyNoChangeRequired": {
givenBucket: &miniov1.Bucket{
ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{
lockAnnotation: "claimed",
}},
Spec: miniov1.BucketSpec{ForProvider: miniov1.BucketParameters{
BucketName: "my-bucket",
Policy: &policy}},
},
policyLatest: true,
bucketExists: true,
expectedResult: managed.ExternalObservation{ResourceExists: true, ResourceUpToDate: true},
expectedBucketObservation: miniov1.BucketProviderStatus{BucketName: "my-bucket"},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
currFn := bucketExistsFn
defer func() {
bucketExistsFn = currFn
}()

bucketPolicyLatestFn = func(ctx context.Context, mc *minio.Client, bucketName string, policy string) (bool, error) {
return tc.policyLatest, tc.returnError
}

bucketExistsFn = func(ctx context.Context, mc *minio.Client, bucketName string) (bool, error) {
return tc.bucketExists, tc.returnError
}
Expand Down
15 changes: 14 additions & 1 deletion operator/bucket/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,25 @@ import (

"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
"github.com/crossplane/crossplane-runtime/pkg/resource"
miniov1 "github.com/vshn/provider-minio/apis/minio/v1"
controllerruntime "sigs.k8s.io/controller-runtime"
)

func (d *bucketClient) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) {
func (b *bucketClient) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) {
log := controllerruntime.LoggerFrom(ctx)
log.V(1).Info("updating resource")

bucket, ok := mg.(*miniov1.Bucket)
if !ok {
return managed.ExternalUpdate{}, errNotBucket
}

if bucket.Spec.ForProvider.Policy != nil {
err := b.mc.SetBucketPolicy(ctx, bucket.GetBucketName(), *bucket.Spec.ForProvider.Policy)
if err != nil {
return managed.ExternalUpdate{}, err
}
}

return managed.ExternalUpdate{}, nil
}
5 changes: 5 additions & 0 deletions package/crds/minio.crossplane.io_buckets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ spec:
follows RFC 1123. Be aware that S3 providers may require a unique
name across the platform or zone.
type: string
policy:
description: Policy is a raw S3 bucket policy. Please consult
https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html
for more details about the policy.
type: string
region:
default: us-east-1
description: Region is the name of the region where the bucket
Expand Down

0 comments on commit f161d9a

Please sign in to comment.