diff --git a/api/v1alpha1/nonadminbackup_types.go b/api/v1alpha1/nonadminbackup_types.go index 8bd9193..7716e2a 100644 --- a/api/v1alpha1/nonadminbackup_types.go +++ b/api/v1alpha1/nonadminbackup_types.go @@ -22,7 +22,7 @@ import ( ) // NonAdminBackupPhase is a simple one high-level summary of the lifecycle of an NonAdminBackup. -// +kubebuilder:validation:Enum=New;BackingOff;Created +// +kubebuilder:validation:Enum=New;BackingOff;Created;Deleted type NonAdminBackupPhase string const ( @@ -32,6 +32,8 @@ const ( NonAdminBackupPhaseBackingOff NonAdminBackupPhase = "BackingOff" // NonAdminBackupPhaseCreated - Velero Backup was created. The Phase will not have additional informations about the Backup. NonAdminBackupPhaseCreated NonAdminBackupPhase = "Created" + // NonAdminBackupPhaseDeleted - Velero Backup was deleted. + NonAdminBackupPhaseDeleted NonAdminBackupPhase = "Deleted" ) // NonAdminBackupSpec defines the desired state of NonAdminBackup @@ -39,6 +41,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 ad4980d..639caa1 100644 --- a/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml +++ b/config/crd/bases/nac.oadp.openshift.io_nonadminbackups.yaml @@ -503,6 +503,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) @@ -595,6 +599,7 @@ spec: - New - BackingOff - Created + - Deleted type: string veleroBackupName: description: VeleroBackupName references the VeleroBackup object by diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 1771d3e..6558bed 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/konveyor/oadp-non-admin - newTag: latest + newName: quay.io/migi/oadp-nac-operator + newTag: v0.0.63 diff --git a/internal/common/function/function.go b/internal/common/function/function.go index 250e58b..950f822 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,29 @@ 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 +} + // 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..b780b7e 100644 --- a/internal/controller/nonadminbackup_controller.go +++ b/internal/controller/nonadminbackup_controller.go @@ -87,6 +87,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 +125,53 @@ 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 { + if nab.Status.Phase == nacv1alpha1.NonAdminBackupPhaseDeleted { + // No change, no need to update + return true, nil + } + + deleted, deleteErr := function.DeleteVeleroBackup(ctx, r.Client, rLog, nab) + if deleteErr != nil { + return true, deleteErr + } + + deletedMsg := "Velero Backup deleted" + deletedCondition := metav1.ConditionTrue + + if !deleted { + deletedCondition = metav1.ConditionFalse + deletedMsg = "Unable to find origin Velero Backup" + } + + _, errUpdateCondition := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionDeletion, deletedCondition, "BackupDeleted", deletedMsg) + if errUpdateCondition != nil { + logger.Error(errUpdateCondition, "Unable to set Deletion Condition: True", nameField, nab.Name, constant.NameSpaceString, nab.Namespace) + return true, errUpdateCondition + } + _, errUpdatePhase := function.UpdateNonAdminPhase(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminBackupPhaseDeleted) + + if errUpdatePhase != nil { + logger.Error(errUpdatePhase, "Unable to set NonAdminBackup Phase: Deleted", nameField, nab.Name, constant.NameSpaceString, nab.Namespace) + return true, errUpdatePhase + } + } + + 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