Added tests for backup types

This commit is contained in:
Martin Montes
2023-12-25 22:10:31 +01:00
parent 0b2cbf5704
commit 05e9c91569
7 changed files with 206 additions and 87 deletions

View File

@ -2,6 +2,7 @@ package v1alpha1
import (
"errors"
"fmt"
"reflect"
"time"
@ -12,23 +13,23 @@ import (
// BackupStorage defines the storage for a Backup.
type BackupStorage struct {
// Volume is a Kubernetes volume specification.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
Volume *corev1.VolumeSource `json:"volume,omitempty"`
// PersistentVolumeClaim is a Kubernetes PVC specification.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
PersistentVolumeClaim *corev1.PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"`
// S3 defines the configuration to store backups in a S3 compatible storage.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
S3 *S3 `json:"s3,omitempty"`
// PersistentVolumeClaim is a Kubernetes PVC specification.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
PersistentVolumeClaim *corev1.PersistentVolumeClaimSpec `json:"persistentVolumeClaim,omitempty"`
// Volume is a Kubernetes volume specification.
// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec
Volume *corev1.VolumeSource `json:"volume,omitempty"`
}
func (s *BackupStorage) Validate() error {
func (b *BackupStorage) Validate() error {
storageTypes := 0
fields := reflect.ValueOf(s).Elem()
fields := reflect.ValueOf(b).Elem()
for i := 0; i < fields.NumField(); i++ {
field := fields.Field(i)
if !field.IsNil() {
@ -134,6 +135,27 @@ func (b *Backup) IsComplete() bool {
return meta.IsStatusConditionTrue(b.Status.Conditions, ConditionTypeComplete)
}
func (b *Backup) SetDefaults() {
if b.Spec.MaxRetention == (metav1.Duration{}) {
b.Spec.MaxRetention = metav1.Duration{Duration: 30 * 24 * time.Hour}
}
if b.Spec.BackoffLimit == 0 {
b.Spec.BackoffLimit = 5
}
}
func (b *Backup) Validate() error {
if b.Spec.Schedule != nil {
if err := b.Spec.Schedule.Validate(); err != nil {
return fmt.Errorf("invalid Schedule: %v", err)
}
}
if err := b.Spec.Storage.Validate(); err != nil {
return fmt.Errorf("invalid Storage: %v", err)
}
return nil
}
func (b *Backup) Volume() (*corev1.VolumeSource, error) {
if b.Spec.Storage.S3 != nil {
return &corev1.VolumeSource{
@ -153,15 +175,6 @@ func (b *Backup) Volume() (*corev1.VolumeSource, error) {
return nil, errors.New("unable to get volume from Backup")
}
func (b *Backup) SetDefaults() {
if b.Spec.MaxRetention == (metav1.Duration{}) {
b.Spec.MaxRetention = metav1.Duration{Duration: 30 * 24 * time.Hour}
}
if b.Spec.BackoffLimit == 0 {
b.Spec.BackoffLimit = 5
}
}
// +kubebuilder:object:root=true
// BackupList contains a list of Backup

View File

@ -5,13 +5,14 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var _ = Describe("Backup types", func() {
objMeta := metav1.ObjectMeta{
Name: "backup-obj",
Namespace: "backup-obj",
Namespace: testNamespace,
}
Context("When creating a Backup object", func() {
DescribeTable(
@ -21,7 +22,7 @@ var _ = Describe("Backup types", func() {
Expect(backup).To(BeEquivalentTo(expected))
},
Entry(
"Emtpty",
"Empty",
&Backup{
ObjectMeta: objMeta,
},
@ -51,5 +52,83 @@ var _ = Describe("Backup types", func() {
},
),
)
DescribeTable(
"Should return a volume",
func(backup *Backup, expectedVolume *corev1.VolumeSource, wantErr bool) {
volume, err := backup.Volume()
if wantErr {
Expect(err).To(HaveOccurred())
} else {
Expect(err).ToNot(HaveOccurred())
}
Expect(volume).To(BeEquivalentTo(expectedVolume))
},
Entry(
"No storage",
&Backup{
ObjectMeta: objMeta,
Spec: BackupSpec{
Storage: BackupStorage{},
},
},
nil,
true,
),
Entry(
"S3",
&Backup{
ObjectMeta: objMeta,
Spec: BackupSpec{
Storage: BackupStorage{
S3: &S3{},
},
},
},
&corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
false,
),
Entry(
"PVC",
&Backup{
ObjectMeta: objMeta,
Spec: BackupSpec{
Storage: BackupStorage{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{},
},
},
},
&corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: objMeta.Name,
},
},
false,
),
Entry(
"Volume",
&Backup{
ObjectMeta: objMeta,
Spec: BackupSpec{
Storage: BackupStorage{
Volume: &corev1.VolumeSource{
NFS: &corev1.NFSVolumeSource{
Server: "test",
Path: "test",
},
},
},
},
},
&corev1.VolumeSource{
NFS: &corev1.NFSVolumeSource{
Server: "test",
Path: "test",
},
},
false,
),
)
})
})

View File

@ -40,33 +40,12 @@ func (r *Backup) ValidateDelete() (admission.Warnings, error) {
}
func (r *Backup) validate() (admission.Warnings, error) {
if err := r.validateSchedule(); err != nil {
return nil, err
}
return nil, r.validateStorage()
}
func (r *Backup) validateSchedule() error {
if r.Spec.Schedule == nil {
return nil
}
if err := r.Spec.Schedule.Validate(); err != nil {
return field.Invalid(
field.NewPath("spec").Child("schedule"),
r.Spec.Schedule,
fmt.Sprintf("invalid schedule: %v", err),
if err := r.Validate(); err != nil {
return nil, field.Invalid(
field.NewPath("spec"),
r.Spec,
fmt.Sprintf("invalid Backup: %v", err),
)
}
return nil
}
func (r *Backup) validateStorage() error {
if err := r.Spec.Storage.Validate(); err != nil {
return field.Invalid(
field.NewPath("spec").Child("storage"),
r.Spec.Storage,
fmt.Sprintf("invalid storage: %v", err),
)
}
return nil
return nil, nil
}

View File

@ -25,7 +25,7 @@ var _ = Describe("Backup webhook", func() {
}
},
Entry(
"Invalid storage",
"No storage",
&Backup{
ObjectMeta: metav1.ObjectMeta{
Name: "backup-invalid-storage",
@ -50,6 +50,73 @@ var _ = Describe("Backup webhook", func() {
},
true,
),
Entry(
"Multiple storages",
&Backup{
ObjectMeta: metav1.ObjectMeta{
Name: "backup-invalid-storage",
Namespace: testNamespace,
},
Spec: BackupSpec{
Storage: BackupStorage{
S3: &S3{
Bucket: "test",
Endpoint: "test",
},
Volume: &corev1.VolumeSource{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
ClaimName: "TEST",
},
},
},
MariaDBRef: MariaDBRef{
ObjectReference: corev1.ObjectReference{
Name: "mariadb-webhook",
},
WaitForIt: true,
},
BackoffLimit: 10,
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
},
},
true,
),
Entry(
"Single storage",
&Backup{
ObjectMeta: metav1.ObjectMeta{
Name: "backup-invalid-storage",
Namespace: testNamespace,
},
Spec: BackupSpec{
Storage: BackupStorage{
S3: &S3{
Bucket: "test",
Endpoint: "test",
},
},
MariaDBRef: MariaDBRef{
ObjectReference: corev1.ObjectReference{
Name: "mariadb-webhook",
},
WaitForIt: true,
},
BackoffLimit: 10,
Resources: &corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"cpu": resource.MustParse("100m"),
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
},
},
false,
),
Entry(
"Invalid schedule",
&Backup{
@ -62,15 +129,9 @@ var _ = Describe("Backup webhook", func() {
Cron: "foo",
},
Storage: BackupStorage{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"storage": resource.MustParse("100Mi"),
},
},
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
S3: &S3{
Bucket: "test",
Endpoint: "test",
},
},
MariaDBRef: MariaDBRef{
@ -102,15 +163,9 @@ var _ = Describe("Backup webhook", func() {
Cron: "*/1 * * * *",
},
Storage: BackupStorage{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"storage": resource.MustParse("100Mi"),
},
},
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
S3: &S3{
Bucket: "test",
Endpoint: "test",
},
},
MariaDBRef: MariaDBRef{
@ -146,15 +201,9 @@ var _ = Describe("Backup webhook", func() {
},
Spec: BackupSpec{
Storage: BackupStorage{
PersistentVolumeClaim: &corev1.PersistentVolumeClaimSpec{
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
"storage": resource.MustParse("100Mi"),
},
},
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
S3: &S3{
Bucket: "test",
Endpoint: "test",
},
},
MariaDBRef: MariaDBRef{
@ -217,8 +266,7 @@ var _ = Describe("Backup webhook", func() {
Entry(
"Updating Storage",
func(bmdb *Backup) {
newStorageClass := "fast-storage"
bmdb.Spec.Storage.PersistentVolumeClaim.StorageClassName = &newStorageClass
bmdb.Spec.Storage.S3.Bucket = "another-bucket"
},
true,
),

View File

@ -174,9 +174,9 @@ func (in *BackupStatus) DeepCopy() *BackupStatus {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BackupStorage) DeepCopyInto(out *BackupStorage) {
*out = *in
if in.Volume != nil {
in, out := &in.Volume, &out.Volume
*out = new(v1.VolumeSource)
if in.S3 != nil {
in, out := &in.S3, &out.S3
*out = new(S3)
(*in).DeepCopyInto(*out)
}
if in.PersistentVolumeClaim != nil {
@ -184,9 +184,9 @@ func (in *BackupStorage) DeepCopyInto(out *BackupStorage) {
*out = new(v1.PersistentVolumeClaimSpec)
(*in).DeepCopyInto(*out)
}
if in.S3 != nil {
in, out := &in.S3, &out.S3
*out = new(S3)
if in.Volume != nil {
in, out := &in.Volume, &out.Volume
*out = new(v1.VolumeSource)
(*in).DeepCopyInto(*out)
}
}

View File

@ -304,7 +304,7 @@ metadata:
categories: Database
certified: "true"
containerImage: mariadb/mariadb-operator-enterprise@sha256:a7bc99c11043b38795b1da6dcb0ecb04a92e6d5db66dd7c058f9c8e772b2ef06
createdAt: "2023-12-25T18:42:20Z"
createdAt: "2023-12-25T21:10:03Z"
operators.openshift.io/valid-subscription: '["MariaDB Enterprise Server License"]'
operators.operatorframework.io/builder: operator-sdk-v1.32.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v3

View File

@ -44,7 +44,7 @@ func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if err := r.setSpecDefaults(ctx, &backup); err != nil {
if err := r.setDefaults(ctx, &backup); err != nil {
return ctrl.Result{}, fmt.Errorf("error defaulting Backup: %v", err)
}
@ -87,7 +87,7 @@ func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
return ctrl.Result{}, nil
}
func (r *BackupReconciler) setSpecDefaults(ctx context.Context, backup *mariadbv1alpha1.Backup) error {
func (r *BackupReconciler) setDefaults(ctx context.Context, backup *mariadbv1alpha1.Backup) error {
return r.patch(ctx, backup, func(b *mariadbv1alpha1.Backup) {
backup.SetDefaults()
})