diff --git a/api/v1alpha1/backupconfiguration_types.go b/api/v1alpha1/backupconfiguration_types.go index 2ce1c14..c315ef2 100644 --- a/api/v1alpha1/backupconfiguration_types.go +++ b/api/v1alpha1/backupconfiguration_types.go @@ -17,7 +17,10 @@ limitations under the License. package v1alpha1 import ( + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" ) // +kubebuilder:validation:Enum=Deployment;StatefulSet;Pod @@ -38,6 +41,22 @@ const ( JobKind BackupType = "Job" ) +func GetTargetObjects(kind TargetKind) (targetObject client.Object, targetPodSpec *corev1.PodSpec) { + switch kind { + case Deployment: + deployment := appsv1.Deployment{} + targetObject = &deployment + targetPodSpec = &deployment.Spec.Template.Spec + + case StatefulSet: + statefulSet := appsv1.StatefulSet{} + targetObject = &statefulSet + targetPodSpec = &statefulSet.Spec.Template.Spec + + } + return +} + const ( BACKUP_PREFIX_PATH = `backup` FORMOL_SHARED_VOLUME = `formol-shared` diff --git a/controllers/backupconfiguration_controller_helpers.go b/controllers/backupconfiguration_controller_helpers.go index c9dcb96..97dc74a 100644 --- a/controllers/backupconfiguration_controller_helpers.go +++ b/controllers/backupconfiguration_controller_helpers.go @@ -18,7 +18,6 @@ package controllers import ( "fmt" - appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -130,36 +129,6 @@ func (r *BackupConfigurationReconciler) AddCronJob(backupConf formolv1alpha1.Bac } } -func (r *BackupConfigurationReconciler) getTargetObjects(kind formolv1alpha1.TargetKind, namespace string, name string) (targetObject client.Object, targetPodSpec *corev1.PodSpec, err error) { - switch kind { - case formolv1alpha1.Deployment: - deployment := appsv1.Deployment{} - if err = r.Get(r.Context, client.ObjectKey{ - Namespace: namespace, - Name: name, - }, &deployment); err != nil { - r.Log.Error(err, "cannot get deployment", "Deployment", name) - return - } - targetObject = &deployment - targetPodSpec = &deployment.Spec.Template.Spec - - case formolv1alpha1.StatefulSet: - statefulSet := appsv1.StatefulSet{} - if err = r.Get(r.Context, client.ObjectKey{ - Namespace: namespace, - Name: name, - }, &statefulSet); err != nil { - r.Log.Error(err, "cannot get StatefulSet", "StatefulSet", name) - return - } - targetObject = &statefulSet - targetPodSpec = &statefulSet.Spec.Template.Spec - - } - return -} - func (r *BackupConfigurationReconciler) DeleteSidecar(backupConf formolv1alpha1.BackupConfiguration) error { removeTags := func(podSpec *corev1.PodSpec, target formolv1alpha1.Target) { for i, container := range podSpec.Containers { @@ -190,9 +159,12 @@ func (r *BackupConfigurationReconciler) DeleteSidecar(backupConf formolv1alpha1. } r.Log.V(1).Info("Got Repository", "repo", repo) for _, target := range backupConf.Spec.Targets { - targetObject, targetPodSpec, err := r.getTargetObjects(target.TargetKind, backupConf.Namespace, target.TargetName) - if err != nil { - r.Log.Error(err, "unable to get target objects") + targetObject, targetPodSpec := formolv1alpha1.GetTargetObjects(target.TargetKind) + if err := r.Get(r.Context, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: target.TargetName, + }, targetObject); err != nil { + r.Log.Error(err, "cannot get target", "target", target.TargetName) return err } restoreContainers := []corev1.Container{} @@ -225,7 +197,7 @@ func (r *BackupConfigurationReconciler) DeleteSidecar(backupConf formolv1alpha1. } targetPodSpec.Volumes = restoreVolumes removeTags(targetPodSpec, target) - if err = r.Update(r.Context, targetObject); err != nil { + if err := r.Update(r.Context, targetObject); err != nil { r.Log.Error(err, "unable to remove sidecar", "targetObject", targetObject) return err } @@ -275,9 +247,12 @@ func (r *BackupConfigurationReconciler) addSidecar(backupConf formolv1alpha1.Bac Privileged: func() *bool { b := true; return &b }(), }, } - targetObject, targetPodSpec, err := r.getTargetObjects(target.TargetKind, backupConf.Namespace, target.TargetName) - if err != nil { - r.Log.Error(err, "unable to get target objects") + targetObject, targetPodSpec := formolv1alpha1.GetTargetObjects(target.TargetKind) + if err := r.Get(r.Context, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: target.TargetName, + }, targetObject); err != nil { + r.Log.Error(err, "cannot get target", "target", target.TargetName) return err } if !hasSidecar(targetPodSpec) { diff --git a/controllers/restoresession_controller.go b/controllers/restoresession_controller.go index fdf9b0b..ea2759a 100644 --- a/controllers/restoresession_controller.go +++ b/controllers/restoresession_controller.go @@ -65,7 +65,33 @@ func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req ctrl.Reque var newSessionState formolv1alpha1.SessionState switch restoreSession.Status.SessionState { case formolv1alpha1.New: - newSessionState = r.initRestore(&restoreSession, backupConf) + // Go through the Targets and create the corresponding TargetStatus. Move to Initializing. + if r.isBackupOngoing(backupConf) { + r.Log.V(0).Info("there is an ongoing backup. Let's reschedule this operation") + return ctrl.Result{ + RequeueAfter: 30 * time.Second, + }, nil + } + restoreSession.Status.Targets = r.initSession(backupConf) + newSessionState = formolv1alpha1.Initializing + case formolv1alpha1.Initializing: + // Wait for all the Targets to be in the Initialized state then move them to Running and move to Running myself. + // if one of the Target fails to initialize, move it back to New state and decrement Try. + // if try reaches 0, move all the Targets to Finalize and move myself to Failure. + newSessionState = r.checkInitialized(restoreSession.Status.Targets, backupConf) + case formolv1alpha1.Running: + // Wait for all the target to be in Waiting state then move them to the Finalize state. Move myself to Finalize. + // if one of the Target fails the backup, move it back to Running state and decrement Try. + // if try reaches 0, move all the Targets to Finalize and move myself to Failure. + newSessionState = r.checkWaiting(restoreSession.Status.Targets, backupConf) + case formolv1alpha1.Finalize: + // Check the TargetStatus of all the Targets. If they are all Success then move myself to Success. + // if one of the Target fails to Finalize, move it back to Finalize state and decrement Try. + // if try reaches 0, move myself to Success because the backup was a Success even if the Finalize failed. + if newSessionState = r.checkSuccess(restoreSession.Status.Targets, backupConf); newSessionState == formolv1alpha1.Failure { + r.Log.V(0).Info("One of the target did not manage to Finalize but the backup is still a Success") + newSessionState = formolv1alpha1.Success + } case "": newSessionState = formolv1alpha1.New restoreSession.Status.StartTime = &metav1.Time{Time: time.Now()}