From c5fb5267aee8fa05586f68da6e3fbdf19beb83d4 Mon Sep 17 00:00:00 2001 From: Michal Pryc Date: Tue, 14 May 2024 15:09:31 +0200 Subject: [PATCH] Delete Velero Backup when user sets the deleteVeleroBackup Enhancement which will delete original VeleroBackup when the user sets the deleteVeleroBackup witin NonAdminBackup Spec. Fixes Issue #58 Signed-off-by: Michal Pryc --- api/v1alpha1/nonadminbackup_types.go | 4 ++ api/v1alpha1/nonadmincontroller_types.go | 1 + api/v1alpha1/zz_generated.deepcopy.go | 5 ++ ...nac.oadp.openshift.io_nonadminbackups.yaml | 4 ++ docs/design/nab_status_update.md | 2 +- internal/common/function/function.go | 36 ++++++++++++ .../controller/nonadminbackup_controller.go | 56 ++++++++++++++++++- 7 files changed, 106 insertions(+), 2 deletions(-) diff --git a/api/v1alpha1/nonadminbackup_types.go b/api/v1alpha1/nonadminbackup_types.go index ea4657a..63ee304 100644 --- a/api/v1alpha1/nonadminbackup_types.go +++ b/api/v1alpha1/nonadminbackup_types.go @@ -39,6 +39,10 @@ type NonAdminBackupSpec struct { // BackupSpec defines the specification for a Velero backup. BackupSpec *velerov1api.BackupSpec `json:"backupSpec,omitempty"` + // DeleteVeleroBackup tells the controller to remove created Velero Backup. + // +optional + DeleteVeleroBackup *bool `json:"deleteVeleroBackup,omitempty"` + // NonAdminBackup log level (use debug for the most logging, leave unset for default) // +optional // +kubebuilder:validation:Enum=trace;debug;info;warning;error;fatal;panic diff --git a/api/v1alpha1/nonadmincontroller_types.go b/api/v1alpha1/nonadmincontroller_types.go index 61c5608..1a54a95 100644 --- a/api/v1alpha1/nonadmincontroller_types.go +++ b/api/v1alpha1/nonadmincontroller_types.go @@ -27,4 +27,5 @@ type NonAdminCondition string const ( NonAdminConditionAccepted NonAdminCondition = "Accepted" NonAdminConditionQueued NonAdminCondition = "Queued" + NonAdminConditionDeletion NonAdminCondition = "Deletion" ) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9ac916b..65e042f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -93,6 +93,11 @@ func (in *NonAdminBackupSpec) DeepCopyInto(out *NonAdminBackupSpec) { *out = new(v1.BackupSpec) (*in).DeepCopyInto(*out) } + if in.DeleteVeleroBackup != nil { + in, out := &in.DeleteVeleroBackup, &out.DeleteVeleroBackup + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NonAdminBackupSpec. diff --git a/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml b/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml index 41dac9e..44a3926 100644 --- a/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml +++ b/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml @@ -505,6 +505,10 @@ spec: type: string type: array type: object + deleteVeleroBackup: + description: DeleteVeleroBackup tells the controller to remove created + Velero Backup. + type: boolean logLevel: description: NonAdminBackup log level (use debug for the most logging, leave unset for default) diff --git a/docs/design/nab_status_update.md b/docs/design/nab_status_update.md index 55ec6b7..4aa792c 100644 --- a/docs/design/nab_status_update.md +++ b/docs/design/nab_status_update.md @@ -108,4 +108,4 @@ class COND_ACCEPTED,COND_QUEUED,COND_ERROR conditions; classDef phases fill:#777,stroke:#ccc,stroke-width:2px; class START,CREATED,ERROR phases; -``` +``` \ No newline at end of file diff --git a/internal/common/function/function.go b/internal/common/function/function.go index 250e58b..ff4badb 100644 --- a/internal/common/function/function.go +++ b/internal/common/function/function.go @@ -27,6 +27,7 @@ import ( "github.com/go-logr/logr" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -280,6 +281,41 @@ func CheckVeleroBackupLabels(backup *velerov1api.Backup) bool { return exists && value == constant.ManagedByLabelValue } +// DeleteVeleroBackup deletes Velero Backup object based on the NonAdminBackup object name and namespace +func DeleteVeleroBackup(ctx context.Context, r client.Client, logger logr.Logger, nab *nacv1alpha1.NonAdminBackup) (bool, error) { + veleroBackupName := GenerateVeleroBackupName(nab.Namespace, nab.Name) + + logger.V(1).Info(fmt.Sprintf("Attempting to delete VeleroBackup: %s", veleroBackupName)) + veleroBackup := velerov1api.Backup{} + + err := r.Get(ctx, types.NamespacedName{Name: veleroBackupName, Namespace: constant.OadpNamespace}, &veleroBackup) + + if err == nil { + err = r.Delete(ctx, &veleroBackup) + if err != nil { + logger.V(1).Info(fmt.Sprintf("Error deleting VeleroBackup: %s", veleroBackupName)) + return false, err + } + } else if apierrors.IsNotFound(err) { + return false, nil + } + + logger.V(1).Info(fmt.Sprintf("Deleted VeleroBackup: %s", veleroBackupName)) + return true, nil +} + +// DeleteNonAdminBackup deletes Non Admin Backup object +func DeleteNonAdminBackup(ctx context.Context, r client.Client, logger logr.Logger, nab *nacv1alpha1.NonAdminBackup) error { + err := r.Delete(ctx, nab) + if err != nil { + logger.V(1).Info(fmt.Sprintf("Error deleting NonAdminBackup: %s", nab.Name)) + return err + } + + logger.V(1).Info(fmt.Sprintf("Deleted NonAdminBackup: %s", nab.Name)) + return nil +} + // TODO not used // GetNonAdminBackupFromVeleroBackup return referenced NonAdminBackup object from Velero Backup object, if no error occurs diff --git a/internal/controller/nonadminbackup_controller.go b/internal/controller/nonadminbackup_controller.go index 85456c2..abc4450 100644 --- a/internal/controller/nonadminbackup_controller.go +++ b/internal/controller/nonadminbackup_controller.go @@ -20,6 +20,7 @@ package controller import ( "context" "errors" + "fmt" "time" "github.com/go-logr/logr" @@ -87,6 +88,13 @@ func (r *NonAdminBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } + reconcileExit, reconcileErr := r.DeleteVeleroBackup(ctx, rLog, &nab) + if reconcileExit && reconcileErr != nil { + return ctrl.Result{}, reconcile.TerminalError(reconcileErr) + } else if reconcileExit { + return ctrl.Result{}, nil + } + reconcileExit, reconcileRequeue, reconcileErr := r.InitNonAdminBackup(ctx, rLog, &nab) if reconcileRequeue { return ctrl.Result{Requeue: true, RequeueAfter: requeueTimeSeconds * time.Second}, reconcileErr @@ -118,6 +126,52 @@ func (r *NonAdminBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, nil } +// DeleteVeleroBackup deletes original Velero Backup object when the deleteVeleroBackup Spec is set to true +// +// Parameters: +// +// ctx: Context for the request. +// logrLogger: Logger instance for logging messages. +// nab: Pointer to the NonAdminBackup object. +func (r *NonAdminBackupReconciler) DeleteVeleroBackup(ctx context.Context, logrLogger logr.Logger, nab *nacv1alpha1.NonAdminBackup) (exitReconcile bool, errorReconcile error) { + rLog := log.FromContext(ctx) + logger := logrLogger.WithValues("DeleteVeleroBackup", nab.Namespace) + + // Delete Velero Backup if the deleteVeleroBackup was set to true + if nab.Spec.DeleteVeleroBackup != nil && *nab.Spec.DeleteVeleroBackup { + deleted, deleteErr := function.DeleteVeleroBackup(ctx, r.Client, rLog, nab) + if !deleted { + // The Velero Object was not found + // Remove NonAdminBackup + // There is potential that Velero Sync controller will recreate Velero Backup + // if one exists in the s3 storage, and this will as well cause NonAdminBackup + // to be recreated by the NAC sync controller, but at this stage we have current + // proper state reflected. + logger.V(1).Info(fmt.Sprintf("Velero Backup not deleted for NAB: %s", nab.Name)) + } + + if deleteErr == nil { + // Delete NonAdminBackup object + deleteErrNab := function.DeleteNonAdminBackup(ctx, r.Client, rLog, nab) + return true, deleteErrNab + } + // There was an error while trying to delete Velero Backup + // Set the Phase to BackingOff and Deletion condition to False + _, errUpdateCondition := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionDeletion, metav1.ConditionFalse, "BackupDeleted", "Unable to delete origin Velero Backup") + if errUpdateCondition != nil { + logger.Error(errUpdateCondition, "Unable to set Deletion Condition: True", nameField, nab.Name, constant.NameSpaceString, nab.Namespace) + } + _, errUpdatePhase := function.UpdateNonAdminPhase(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminBackupPhaseBackingOff) + + if errUpdatePhase != nil { + logger.Error(errUpdatePhase, "Unable to set NonAdminBackup Phase: BackingOff", nameField, nab.Name, constant.NameSpaceString, nab.Namespace) + } + return true, deleteErr + } + + return false, nil +} + // InitNonAdminBackup sets the New Phase on a NonAdminBackup object if it is not already set. // // Parameters: @@ -201,7 +255,7 @@ func (r *NonAdminBackupReconciler) ValidateVeleroBackupSpec(ctx context.Context, return true, false, err } - updatedStatus, errUpdateStatus := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionAccepted, metav1.ConditionTrue, "BackupAccepted", "backup accepted") + updatedStatus, errUpdateStatus := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionAccepted, metav1.ConditionTrue, "BackupAccepted", "Backup accepted") if errUpdateStatus != nil { logger.Error(errUpdateStatus, "Unable to set BackupAccepted Condition: True", nameField, nab.Name, constant.NameSpaceString, nab.Namespace) return true, false, errUpdateStatus