Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions api/v1alpha1/bucket_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ type BucketSpec struct {
// Quota to apply to the bucket
// +kubebuilder:validation:Required
Quota Quota `json:"quota"`

// Enable object locking on the bucket. Must be set at creation time.
// Enables versioning automatically.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="objectLocking is immutable"
ObjectLocking bool `json:"objectLocking,omitempty"`

// Default retention configuration for the bucket. Requires objectLocking to be true.
// +kubebuilder:validation:Optional
Retention *RetentionSpec `json:"retention,omitempty"`
}

// RetentionSpec defines the default retention policy for a locked bucket.
type RetentionSpec struct {
// Retention mode: "governance" or "compliance"
// +kubebuilder:validation:Enum=governance;compliance
// +kubebuilder:validation:Required
Mode string `json:"mode"`

// Retention period in days
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Required
Days uint `json:"days"`
}

// BucketStatus defines the observed state of Bucket
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

28 changes: 27 additions & 1 deletion config/crd/bases/s3.onyxia.sh_buckets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.1
name: buckets.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down Expand Up @@ -42,6 +42,14 @@ spec:
name:
description: Name of the bucket
type: string
objectLocking:
description: |-
Enable object locking on the bucket. Must be set at creation time.
Enables versioning automatically.
type: boolean
x-kubernetes-validations:
- message: objectLocking is immutable
rule: self == oldSelf
paths:
description: Paths (folders) to create inside the bucket
items:
Expand All @@ -67,6 +75,24 @@ spec:
required:
- default
type: object
retention:
description: Default retention configuration for the bucket. Requires
objectLocking to be true.
properties:
days:
description: Retention period in days
minimum: 1
type: integer
mode:
description: 'Retention mode: "governance" or "compliance"'
enum:
- governance
- compliance
type: string
required:
- days
- mode
type: object
s3InstanceRef:
default: s3-operator/default
description: s3InstanceRef where create the bucket
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/s3.onyxia.sh_paths.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.1
name: paths.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/s3.onyxia.sh_policies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.1
name: policies.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/s3.onyxia.sh_s3instances.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.1
name: s3instances.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down
2 changes: 1 addition & 1 deletion config/crd/bases/s3.onyxia.sh_s3users.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.1
controller-gen.kubebuilder.io/version: v0.20.1
name: s3users.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down
36 changes: 28 additions & 8 deletions deploy/charts/s3-operator/templates/crds/buckets.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
{{- if .Values.crds.install }}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
{{- if .Values.crds.keep }}
helm.sh/resource-policy: keep
{{- end }}
controller-gen.kubebuilder.io/version: v0.17.1
labels:
{{- include "s3-operator.labels" . | nindent 4 }}
controller-gen.kubebuilder.io/version: v0.20.1
name: buckets.s3.onyxia.sh
spec:
group: s3.onyxia.sh
Expand Down Expand Up @@ -47,6 +42,14 @@ spec:
name:
description: Name of the bucket
type: string
objectLocking:
description: |-
Enable object locking on the bucket. Must be set at creation time.
Enables versioning automatically.
type: boolean
x-kubernetes-validations:
- message: objectLocking is immutable
rule: self == oldSelf
paths:
description: Paths (folders) to create inside the bucket
items:
Expand All @@ -72,6 +75,24 @@ spec:
required:
- default
type: object
retention:
description: Default retention configuration for the bucket. Requires
objectLocking to be true.
properties:
days:
description: Retention period in days
minimum: 1
type: integer
mode:
description: 'Retention mode: "governance" or "compliance"'
enum:
- governance
- compliance
type: string
required:
- days
- mode
type: object
s3InstanceRef:
default: s3-operator/default
description: s3InstanceRef where create the bucket
Expand Down Expand Up @@ -155,4 +176,3 @@ spec:
storage: true
subresources:
status: {}
{{- end }}
29 changes: 28 additions & 1 deletion internal/controller/bucket/reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ func (r *BucketReconciler) handleCreation(
}

// Bucket creation
err = s3Client.CreateBucket(bucketResource.Spec.Name)
err = s3Client.CreateBucket(bucketResource.Spec.Name, bucketResource.Spec.ObjectLocking)
if err != nil {
logger.Error(
err,
Expand Down Expand Up @@ -505,6 +505,33 @@ func (r *BucketReconciler) handleCreation(
)
}

// Retention configuration (requires objectLocking)
if bucketResource.Spec.Retention != nil && bucketResource.Spec.ObjectLocking {
err = s3Client.SetBucketRetention(
bucketResource.Spec.Name,
bucketResource.Spec.Retention.Mode,
bucketResource.Spec.Retention.Days,
)
if err != nil {
logger.Error(
err,
"An error occurred while setting retention for bucket",
"bucketName",
bucketResource.Spec.Name,
"NamespacedName",
req.Namespace,
)
return r.SetReconciledCondition(
ctx,
req,
bucketResource,
s3v1alpha1.CreationFailure,
"An error occurred while setting retention on bucket",
err,
)
}
}

// Path creation
for _, pathInCr := range bucketResource.Spec.Paths {
err = s3Client.CreatePath(bucketResource.Spec.Name, pathInCr)
Expand Down
28 changes: 25 additions & 3 deletions internal/s3/client/impl/minioS3Client.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,35 @@ func (minioS3Client *MinioS3Client) BucketExists(name string) (bool, error) {
return minioS3Client.client.BucketExists(context.Background(), name)
}

func (minioS3Client *MinioS3Client) CreateBucket(name string) error {
func (minioS3Client *MinioS3Client) CreateBucket(name string, objectLocking bool) error {
s3Logger := ctrl.Log.WithValues("logger", "s3clientimplminio")
s3Logger.Info("creating bucket", "bucket", name)
s3Logger.Info("creating bucket", "bucket", name, "objectLocking", objectLocking)
return minioS3Client.client.MakeBucket(
context.Background(),
name,
minio.MakeBucketOptions{Region: minioS3Client.s3Config.Region},
minio.MakeBucketOptions{Region: minioS3Client.s3Config.Region, ObjectLocking: objectLocking},
)
}

func (minioS3Client *MinioS3Client) SetBucketRetention(name string, mode string, days uint) error {
s3Logger := ctrl.Log.WithValues("logger", "s3clientimplminio")
s3Logger.Info("setting bucket retention", "bucket", name, "mode", mode, "days", days)
var retentionMode minio.RetentionMode
switch mode {
case "governance":
retentionMode = minio.Governance
case "compliance":
retentionMode = minio.Compliance
default:
retentionMode = minio.Governance
}
unit := minio.Days
return minioS3Client.client.SetBucketObjectLockConfig(
context.Background(),
name,
&retentionMode,
&days,
&unit,
)
}

Expand Down
10 changes: 8 additions & 2 deletions internal/s3/client/impl/mockedS3Client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,15 @@ func (mockedS3Provider *MockedS3Client) BucketExists(name string) (bool, error)
return false, nil
}

func (mockedS3Provider *MockedS3Client) CreateBucket(name string) error {
func (mockedS3Provider *MockedS3Client) CreateBucket(name string, objectLocking bool) error {
s3Logger := ctrl.Log.WithValues("logger", "s3ClientImplMocked")
s3Logger.Info("checking a bucket", "bucket", name)
s3Logger.Info("creating a bucket", "bucket", name, "objectLocking", objectLocking)
return nil
}

func (mockedS3Provider *MockedS3Client) SetBucketRetention(name string, mode string, days uint) error {
s3Logger := ctrl.Log.WithValues("logger", "s3ClientImplMocked")
s3Logger.Info("setting bucket retention", "bucket", name, "mode", mode, "days", days)
return nil
}

Expand Down
3 changes: 2 additions & 1 deletion internal/s3/client/s3client.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ type S3Config struct {

type S3Client interface {
BucketExists(name string) (bool, error)
CreateBucket(name string) error
CreateBucket(name string, objectLocking bool) error
SetBucketRetention(name string, mode string, days uint) error
DeleteBucket(name string) error
CreatePath(bucketname string, path string) error
PathExists(bucketname string, path string) (bool, error)
Expand Down
13 changes: 10 additions & 3 deletions test/mocks/mockedS3Client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,17 @@ func (mockedS3Provider *MockedS3Client) BucketExists(name string) (bool, error)
return args.Bool(0), args.Error(1)
}

func (mockedS3Provider *MockedS3Client) CreateBucket(name string) error {
func (mockedS3Provider *MockedS3Client) CreateBucket(name string, objectLocking bool) error {
s3Logger := ctrl.Log.WithValues("logger", "mockedS3Client")
s3Logger.Info("checking a bucket", "bucket", name)
args := mockedS3Provider.Called(name)
s3Logger.Info("creating a bucket", "bucket", name, "objectLocking", objectLocking)
args := mockedS3Provider.Called(name, objectLocking)
return args.Error(0)
}

func (mockedS3Provider *MockedS3Client) SetBucketRetention(name string, mode string, days uint) error {
s3Logger := ctrl.Log.WithValues("logger", "mockedS3Client")
s3Logger.Info("setting bucket retention", "bucket", name, "mode", mode, "days", days)
args := mockedS3Provider.Called(name, mode, days)
return args.Error(0)
}

Expand Down
4 changes: 2 additions & 2 deletions test/utils/testUtils.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (t *TestUtils) SetupMockedS3FactoryAndClient() {
})
mockedS3Client.On("BucketExists", "test-bucket").Return(false, nil)
mockedS3Client.On("BucketExists", "existing-bucket").Return(true, nil)
mockedS3Client.On("CreateBucket", "test-bucket").Return(nil)
mockedS3Client.On("CreateBucket", "test-bucket", false).Return(nil)
mockedS3Client.On("SetQuota", "test-bucket", int64(10)).Return(nil)
mockedS3Client.On("ListBuckets").Return([]string{}, nil)
mockedS3Client.On("GetPolicyInfo", "example-policy").Return(nil, nil)
Expand Down Expand Up @@ -127,7 +127,7 @@ func (t *TestUtils) SetupMockedS3FactoryAndClient() {

mockedInvalidS3Client := mocks.NewMockedS3Client(s3client.S3Config{})
mockedInvalidS3Client.On("BucketExists", "test-bucket").Return(false, nil)
mockedInvalidS3Client.On("CreateBucket", "test-bucket").Return(nil)
mockedInvalidS3Client.On("CreateBucket", "test-bucket", false).Return(nil)
mockedInvalidS3Client.On("SetQuota", "test-bucket", int64(10)).Return(nil)

mockedInvalidS3Client.On("ListBuckets").Return([]string{}, fmt.Errorf("random error"))
Expand Down