Skip to content
Merged
12 changes: 12 additions & 0 deletions apis/dataprotection/v1alpha1/backup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,18 @@ type BackupStatus struct {
// +optional
VolumeSnapshots []VolumeSnapshotStatus `json:"volumeSnapshots,omitempty"`

// Records the parent backup name for incremental or differential backup.
// When the parent backup is deleted, the backup will also be deleted.
//
// +optional
ParentBackupName string `json:"parentBackupName,omitempty"`

// Records the base full backup name for incremental backup or differential backup.
// When the base backup is deleted, the backup will also be deleted.
//
// +optional
BaseBackupName string `json:"baseBackupName,omitempty"`

// Records any additional information for the backup.
//
// +optional
Expand Down
6 changes: 6 additions & 0 deletions apis/dataprotection/v1alpha1/backuppolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,12 @@ type BackupMethod struct {
// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
Name string `json:"name"`

// The name of the compatible full backup method, used by incremental backups.
//
// +optional
// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
CompatibleMethod string `json:"compatibleMethod,omitempty"`

// Specifies whether to take snapshots of persistent volumes. If true,
// the ActionSetName is not required, the controller will use the CSI volume
// snapshotter to create the snapshot.
Expand Down
6 changes: 6 additions & 0 deletions apis/dataprotection/v1alpha1/backuppolicytemplate_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ type BackupMethodTPL struct {
// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
Name string `json:"name"`

// The name of the compatible full backup method, used by incremental backups.
//
// +optional
// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
CompatibleMethod string `json:"compatibleMethod,omitempty"`

// Specifies whether to take snapshots of persistent volumes. If true,
// the ActionSetName is not required, the controller will use the CSI volume
// snapshotter to create the snapshot.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ spec:
For volume snapshot backup, the actionSet is not required, the controller
will use the CSI volume snapshotter to create the snapshot.
type: string
compatibleMethod:
description: The name of the compatible full backup method,
used by incremental backups.
pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
type: string
env:
description: Specifies the environment variables for the backup
workload.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ spec:
For volume snapshot backup, the actionSet is not required, the controller
will use the CSI volume snapshotter to create the snapshot.
type: string
compatibleMethod:
description: The name of the compatible full backup method,
used by incremental backups.
pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
type: string
env:
description: Specifies the environment variables for the backup
workload.
Expand Down
15 changes: 15 additions & 0 deletions config/crd/bases/dataprotection.kubeblocks.io_backups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ spec:
For volume snapshot backup, the actionSet is not required, the controller
will use the CSI volume snapshotter to create the snapshot.
type: string
compatibleMethod:
description: The name of the compatible full backup method, used
by incremental backups.
pattern: ^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$
type: string
env:
description: Specifies the environment variables for the backup
workload.
Expand Down Expand Up @@ -1013,6 +1018,11 @@ spec:
backupRepoName:
description: The name of the backup repository.
type: string
baseBackupName:
description: |-
Records the base full backup name for incremental backup or differential backup.
When the base backup is deleted, the backup will also be deleted.
type: string
completionTimestamp:
description: |-
Records the time when the backup operation was completed.
Expand Down Expand Up @@ -1092,6 +1102,11 @@ spec:
kopiaRepoPath:
description: Records the path of the Kopia repository.
type: string
parentBackupName:
description: |-
Records the parent backup name for incremental or differential backup.
When the parent backup is deleted, the backup will also be deleted.
type: string
path:
description: |-
The directory within the backup repository where the backup data is stored.
Expand Down
11 changes: 6 additions & 5 deletions controllers/apps/transformer_cluster_backup_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,11 +414,12 @@ func (r *backupPolicyBuilder) syncBackupMethods(backupPolicy *dpv1alpha1.BackupP
}
for _, backupMethodTPL := range r.backupPolicyTPL.Spec.BackupMethods {
backupMethod := dpv1alpha1.BackupMethod{
Name: backupMethodTPL.Name,
ActionSetName: backupMethodTPL.ActionSetName,
SnapshotVolumes: backupMethodTPL.SnapshotVolumes,
TargetVolumes: backupMethodTPL.TargetVolumes,
RuntimeSettings: backupMethodTPL.RuntimeSettings,
Name: backupMethodTPL.Name,
CompatibleMethod: backupMethodTPL.CompatibleMethod,
ActionSetName: backupMethodTPL.ActionSetName,
SnapshotVolumes: backupMethodTPL.SnapshotVolumes,
TargetVolumes: backupMethodTPL.TargetVolumes,
RuntimeSettings: backupMethodTPL.RuntimeSettings,
}
if m, ok := oldBackupMethodMap[backupMethodTPL.Name]; ok {
backupMethod = m
Expand Down
4 changes: 2 additions & 2 deletions controllers/dataprotection/actionset_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ var _ = Describe("ActionSet Controller test", func() {

Context("create a actionSet", func() {
It("should be available", func() {
as := testdp.NewFakeActionSet(&testCtx)
as := testdp.NewFakeActionSet(&testCtx, nil)
Expect(as).ShouldNot(BeNil())
})
})

Context("validate a actionSet", func() {
It("validate withParameters", func() {
as := testdp.NewFakeActionSet(&testCtx)
as := testdp.NewFakeActionSet(&testCtx, nil)
Expect(as).ShouldNot(BeNil())
By("set invalid withParameters and schema")
Expect(testapps.ChangeObj(&testCtx, as, func(action *dpv1alpha1.ActionSet) {
Expand Down
62 changes: 62 additions & 0 deletions controllers/dataprotection/backup_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ func (r *BackupReconciler) deleteBackupFiles(reqCtx intctrlutil.RequestCtx, back
// handleDeletingPhase handles the deletion of backup. It will delete the backup CR
// and the backup workload(job).
func (r *BackupReconciler) handleDeletingPhase(reqCtx intctrlutil.RequestCtx, backup *dpv1alpha1.Backup) (ctrl.Result, error) {
// delete related backups
if err := r.deleteRelatedBackups(reqCtx, backup); err != nil {
return intctrlutil.RequeueWithError(err, reqCtx.Log, "")
}

// if backup phase is Deleting, delete the backup reference workloads,
// backup data stored in backup repository and volume snapshots.
// TODO(ldm): if backup is being used by restore, do not delete it.
Expand Down Expand Up @@ -439,6 +444,37 @@ func (r *BackupReconciler) prepareBackupRequest(
}
}
request.BackupMethod = backupMethod
// get and validate parent backup
if request.GetBackupType() == string(dpv1alpha1.BackupTypeIncremental) {
request.ParentBackup, err = GetParentBackup(reqCtx.Ctx, r.Client, request.Backup, request.BackupPolicy)
if err != nil {
return nil, err
}
parentBackupType, err := dputils.GetBackupTypeByMethodName(reqCtx, r.Client, request.ParentBackup.Spec.BackupMethod, request.BackupPolicy)
if err != nil {
return nil, err
}
switch parentBackupType {
case dpv1alpha1.BackupTypeFull:
request.BaseBackup = request.ParentBackup
case dpv1alpha1.BackupTypeIncremental:
baseBackup := &dpv1alpha1.Backup{}
baseBackupName := request.ParentBackup.Status.BaseBackupName
if len(baseBackupName) == 0 {
return nil, fmt.Errorf("backup %s/%s base backup name is empty", request.ParentBackup.Namespace, request.ParentBackup.Name)
}
if err := request.Client.Get(reqCtx.Ctx, client.ObjectKey{Name: baseBackupName,
Namespace: request.ParentBackup.Namespace}, baseBackup); err != nil {
return nil, fmt.Errorf("failed to get base backup %s/%s: %w", request.ParentBackup.Namespace, baseBackupName, err)
}
request.BaseBackup = baseBackup
default:
return nil, fmt.Errorf("parent backup type is %s, but only full and incremental backup are supported", parentBackupType)
}
// set status parent backup name and base backup name
request.Status.ParentBackupName = request.ParentBackup.Name
request.Status.BaseBackupName = request.BaseBackup.Name
}
return request, nil
}

Expand Down Expand Up @@ -751,6 +787,32 @@ func (r *BackupReconciler) deleteExternalResources(
return deleteRelatedObjectList(reqCtx, r.Client, &appsv1.StatefulSetList{}, namespaces, labels)
}

// deleteRelatedBackups deletes the related backups.
func (r *BackupReconciler) deleteRelatedBackups(
reqCtx intctrlutil.RequestCtx,
backup *dpv1alpha1.Backup) error {
backupList := &dpv1alpha1.BackupList{}
labels := map[string]string{
dptypes.BackupPolicyLabelKey: backup.Spec.BackupPolicyName,
}
if err := r.Client.List(reqCtx.Ctx, backupList,
client.InNamespace(backup.Namespace), client.MatchingLabels(labels)); client.IgnoreNotFound(err) != nil {
return err
}
for i := range backupList.Items {
bp := &backupList.Items[i]
// delete backups related to the current backup
if bp.Status.ParentBackupName != backup.Name && bp.Status.BaseBackupName != backup.Name {
continue
}
if err := intctrlutil.BackgroundDeleteObject(r.Client, reqCtx.Ctx, bp); err != nil {
return err
}
reqCtx.Log.Info("delete the related backup", "backup", fmt.Sprintf("%s/%s", bp.Namespace, bp.Name))
}
return nil
}

// PatchBackupObjectMeta patches backup object metaObject include cluster snapshot.
func PatchBackupObjectMeta(
original *dpv1alpha1.Backup,
Expand Down
Loading
Loading