diff --git a/controllers/backupconfiguration_controller.go b/controllers/backupconfiguration_controller.go index 4c7964f..6140854 100644 --- a/controllers/backupconfiguration_controller.go +++ b/controllers/backupconfiguration_controller.go @@ -33,7 +33,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" - //"sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1" ) @@ -45,6 +45,8 @@ type BackupConfigurationReconciler struct { Scheme *runtime.Scheme } +var _ reconcile.Reconciler = &BackupConfigurationReconciler{} + // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=*,verbs=* // +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=apps,resources=replicasets,verbs=get;list;watch;create;update;patch;delete @@ -57,9 +59,8 @@ type BackupConfigurationReconciler struct { // +kubebuilder:rbac:groups=batch,resources=cronjobs,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=batch,resources=cronjobs/status,verbs=get -func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { +func (r *BackupConfigurationReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { var changed bool - ctx := context.Background() log := r.Log.WithValues("backupconfiguration", req.NamespacedName) //time.Sleep(300 * time.Millisecond) @@ -67,7 +68,7 @@ func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result backupConf := &formolv1alpha1.BackupConfiguration{} if err := r.Get(ctx, req.NamespacedName, backupConf); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } getDeployment := func(namespace string, name string) (*appsv1.Deployment, error) { @@ -307,12 +308,12 @@ func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result backupConf.ObjectMeta.Finalizers = formolutils.RemoveString(backupConf.ObjectMeta.Finalizers, finalizerName) if err := r.Update(context.Background(), backupConf); err != nil { log.Error(err, "unable to remove finalizer") - return ctrl.Result{}, err + return reconcile.Result{}, err } } // We have been deleted. Return here log.V(0).Info("backupconf deleted", "backupconf", backupConf.Name) - return ctrl.Result{}, nil + return reconcile.Result{}, nil } // Add finalizer @@ -322,11 +323,11 @@ func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result if err != nil { log.Error(err, "unable to append finalizer") } - return ctrl.Result{}, err + return reconcile.Result{}, err } if err := addCronJob(); err != nil { - return ctrl.Result{}, nil + return reconcile.Result{}, nil } else { backupConf.Status.ActiveCronJob = true } @@ -335,7 +336,7 @@ func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result switch target.Kind { case formolv1alpha1.SidecarKind: if err := addSidecarContainer(target); err != nil { - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } else { backupConf.Status.ActiveSidecar = true } @@ -347,11 +348,11 @@ func (r *BackupConfigurationReconciler) Reconcile(req ctrl.Request) (ctrl.Result log.V(1).Info("updating backupconf") if err := r.Status().Update(ctx, backupConf); err != nil { log.Error(err, "unable to update backupconf", "backupconf", backupConf) - return ctrl.Result{}, err + return reconcile.Result{}, err } } - return ctrl.Result{}, nil + return reconcile.Result{}, nil } func (r *BackupConfigurationReconciler) SetupWithManager(mgr ctrl.Manager) error { diff --git a/controllers/backupsession_controller.go b/controllers/backupsession_controller.go index 94e39a1..5fb974f 100644 --- a/controllers/backupsession_controller.go +++ b/controllers/backupsession_controller.go @@ -32,6 +32,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1" formolutils "github.com/desmo999r/formol/pkg/utils" @@ -50,19 +51,20 @@ type BackupSessionReconciler struct { Scheme *runtime.Scheme } +var _ reconcile.Reconciler = &BackupSessionReconciler{} + // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=backupsessions,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=backupsessions/status,verbs=get;update;patch;create;delete // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=functions,verbs=get;list;watch // +kubebuilder:rbac:groups=batch,resources=jobs,verbs=get;list;create;update;patch;delete;watch -func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { +func (r *BackupSessionReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { log := r.Log.WithValues("backupsession", req.NamespacedName) - ctx := context.Background() backupSession := &formolv1alpha1.BackupSession{} if err := r.Get(ctx, req.NamespacedName, backupSession); err != nil { log.Error(err, "unable to get backupsession") - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } backupConf := &formolv1alpha1.BackupConfiguration{} if err := r.Get(ctx, client.ObjectKey{ @@ -70,7 +72,7 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro Name: backupSession.Spec.Ref.Name, }, backupConf); err != nil { log.Error(err, "unable to get backupConfiguration") - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } // helper functions @@ -358,23 +360,23 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro if err != nil { log.Error(err, "unable to add finalizer") } - return ctrl.Result{}, err + return reconcile.Result{}, err } // Brand new backupsession if isBackupOngoing() { log.V(0).Info("There is an ongoing backup. Let's reschedule this operation") - return ctrl.Result{RequeueAfter: 30 * time.Second}, nil + return reconcile.Result{RequeueAfter: 30 * time.Second}, nil } // start the first task backupSession.Status.SessionState = formolv1alpha1.Running targetStatus, err := startNextTask() if err != nil { - return ctrl.Result{}, err + return reconcile.Result{}, err } log.V(0).Info("New backup. Start the first task", "task", targetStatus) if err := r.Status().Update(ctx, backupSession); err != nil { log.Error(err, "unable to update BackupSession status") - return ctrl.Result{}, err + return reconcile.Result{}, err } case formolv1alpha1.Running: // Backup ongoing. Check the status of the last task to decide what to do @@ -388,7 +390,7 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro targetStatus, err := startNextTask() log.V(0).Info("last task was a success. start a new one", "currentTargetStatus", currentTargetStatus, "targetStatus", targetStatus) if err != nil { - return ctrl.Result{}, err + return reconcile.Result{}, err } if targetStatus == nil { // No more task to start. The backup is a success @@ -398,7 +400,7 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro } if err := r.Status().Update(ctx, backupSession); err != nil { log.Error(err, "unable to update BackupSession status") - return ctrl.Result{}, err + return reconcile.Result{}, err } case formolv1alpha1.Failure: // last task failed. Try to run it again @@ -413,7 +415,7 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro if err := createBackupJob(currentTarget); err != nil { log.V(0).Info("unable to create task", "task", currentTarget) currentTargetStatus.SessionState = formolv1alpha1.Failure - return ctrl.Result{}, err + return reconcile.Result{}, err } } } else { @@ -422,7 +424,7 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro } if err := r.Status().Update(ctx, backupSession); err != nil { log.Error(err, "unable to update BackupSession status") - return ctrl.Result{}, err + return reconcile.Result{}, err } } case formolv1alpha1.Success: @@ -435,29 +437,29 @@ func (r *BackupSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, erro backupSession.Status.StartTime = &metav1.Time{Time: time.Now()} if err := r.Status().Update(ctx, backupSession); err != nil { log.Error(err, "unable to update backupSession") - return ctrl.Result{}, err + return reconcile.Result{}, err } } } else { log.V(0).Info("backupsession being deleted", "backupsession", backupSession.Name) if controllerutil.ContainsFinalizer(backupSession, finalizerName) { if err := deleteExternalResources(); err != nil { - return ctrl.Result{}, err + return reconcile.Result{}, err } } controllerutil.RemoveFinalizer(backupSession, finalizerName) if err := r.Update(ctx, backupSession); err != nil { log.Error(err, "unable to remove finalizer") - return ctrl.Result{}, err + return reconcile.Result{}, err } // We have been deleted. Return here - return ctrl.Result{}, nil + return reconcile.Result{}, nil } - return ctrl.Result{}, nil + return reconcile.Result{}, nil } func (r *BackupSessionReconciler) SetupWithManager(mgr ctrl.Manager) error { - if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &formolv1alpha1.BackupSession{}, sessionState, func(rawObj runtime.Object) []string { + if err := mgr.GetFieldIndexer().IndexField(context.TODO(), &formolv1alpha1.BackupSession{}, sessionState, func(rawObj client.Object) []string { session := rawObj.(*formolv1alpha1.BackupSession) return []string{string(session.Status.SessionState)} }); err != nil { diff --git a/controllers/restoresession_controller.go b/controllers/restoresession_controller.go index 440c8c7..70941ab 100644 --- a/controllers/restoresession_controller.go +++ b/controllers/restoresession_controller.go @@ -31,6 +31,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1" formolutils "github.com/desmo999r/formol/pkg/utils" @@ -39,6 +41,7 @@ import ( const ( RESTORESESSION string = "restoresession" UPDATESTATUS string = "updatestatus" + jobOwnerKey string = ".metadata.controller" ) // RestoreSessionReconciler reconciles a RestoreSession object @@ -48,20 +51,21 @@ type RestoreSessionReconciler struct { Scheme *runtime.Scheme } +var _ reconcile.Reconciler = &RestoreSessionReconciler{} + // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=restoresessions,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=formol.desmojim.fr,resources=restoresessions/status,verbs=get;update;patch -func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { - ctx := context.Background() - log := r.Log.WithValues("restoresession", req.NamespacedName) +func (r *RestoreSessionReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + log := log.FromContext(ctx).WithValues("restoresession", req.NamespacedName) // Get the RestoreSession restoreSession := &formolv1alpha1.RestoreSession{} if err := r.Get(ctx, req.NamespacedName, restoreSession); err != nil { log.Error(err, "unable to get restoresession") - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } - log.V(1).Info("got restoresession", "restoreSession", restoreSession) + log = r.Log.WithValues("restoresession", req.NamespacedName, "version", restoreSession.ObjectMeta.ResourceVersion) // Get the BackupSession the RestoreSession references backupSession := &formolv1alpha1.BackupSession{} if err := r.Get(ctx, client.ObjectKey{ @@ -73,10 +77,10 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err Spec: restoreSession.Spec.BackupSessionRef.Spec, Status: restoreSession.Spec.BackupSessionRef.Status, } - log.V(1).Info("generated backupsession", "backupsession", backupSession) + log.V(1).Info("generated backupsession", "spec", backupSession.Spec, "status", backupSession.Status) } else { log.Error(err, "unable to get backupsession", "restoresession", restoreSession.Spec) - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } } // Get the BackupConfiguration linked to the BackupSession @@ -86,11 +90,24 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err Name: backupSession.Spec.Ref.Name, }, backupConf); err != nil { log.Error(err, "unable to get backupConfiguration", "name", backupSession.Spec.Ref, "namespace", backupSession.Namespace) - return ctrl.Result{}, client.IgnoreNotFound(err) + return reconcile.Result{}, client.IgnoreNotFound(err) } // Helper functions - createRestoreJob := func(target formolv1alpha1.Target) error { + createRestoreJob := func(target formolv1alpha1.Target, snapshotId string) error { + // TODO: Get the list of existing jobs and see if there is already one scheduled for the target + var jobList batchv1.JobList + if err := r.List(ctx, &jobList, client.InNamespace(restoreSession.Namespace), client.MatchingFields{jobOwnerKey: restoreSession.Name}); err != nil { + log.Error(err, "unable to get job list") + return err + } + log.V(1).Info("Found jobs", "jobs", jobList.Items) + for _, job := range jobList.Items { + if job.Annotations["targetName"] == target.Name && job.Annotations["snapshotId"] == snapshotId { + log.V(0).Info("there is already a cronjob to restore that target", "targetName", target.Name, "snapshotId", snapshotId) + return nil + } + } restoreSessionEnv := []corev1.EnvVar{ corev1.EnvVar{ Name: "TARGET_NAME", @@ -110,91 +127,95 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err Name: "output", MountPath: "/output", } - for _, targetStatus := range backupSession.Status.Targets { - if targetStatus.Name == target.Name { - snapshotId := targetStatus.SnapshotId - restic := corev1.Container{ - Name: "restic", - Image: "desmo999r/formolcli:latest", - Args: []string{"volume", "restore", "--snapshot-id", snapshotId}, - VolumeMounts: []corev1.VolumeMount{output}, - Env: restoreSessionEnv, - } - finalizer := corev1.Container{ - Name: "finalizer", - Image: "desmo999r/formolcli:latest", - Args: []string{"target", "finalize"}, - VolumeMounts: []corev1.VolumeMount{output}, - Env: restoreSessionEnv, - } - repo := &formolv1alpha1.Repo{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: backupConf.Namespace, - Name: backupConf.Spec.Repository, - }, repo); err != nil { - log.Error(err, "unable to get Repo from BackupConfiguration") - return err - } - // S3 backing storage - var ttl int32 = 300 - restic.Env = append(restic.Env, formolutils.ConfigureResticEnvVar(backupConf, repo)...) - job := &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf("%s-%s-", restoreSession.Name, target.Name), - Namespace: restoreSession.Namespace, - }, - Spec: batchv1.JobSpec{ - TTLSecondsAfterFinished: &ttl, - Template: corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - InitContainers: []corev1.Container{restic}, - Containers: []corev1.Container{finalizer}, - Volumes: []corev1.Volume{ - corev1.Volume{Name: "output"}, - }, - RestartPolicy: corev1.RestartPolicyOnFailure, - }, + //for _, targetStatus := range backupSession.Status.Targets { + //if targetStatus.Name == target.Name { + //snapshotId := targetStatus.SnapshotId + restic := corev1.Container{ + Name: "restic", + Image: "desmo999r/formolcli:latest", + Args: []string{"volume", "restore", "--snapshot-id", snapshotId}, + VolumeMounts: []corev1.VolumeMount{output}, + Env: restoreSessionEnv, + } + finalizer := corev1.Container{ + Name: "finalizer", + Image: "desmo999r/formolcli:latest", + Args: []string{"target", "finalize"}, + VolumeMounts: []corev1.VolumeMount{output}, + Env: restoreSessionEnv, + } + repo := &formolv1alpha1.Repo{} + if err := r.Get(ctx, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: backupConf.Spec.Repository, + }, repo); err != nil { + log.Error(err, "unable to get Repo from BackupConfiguration") + return err + } + // S3 backing storage + var ttl int32 = 300 + restic.Env = append(restic.Env, formolutils.ConfigureResticEnvVar(backupConf, repo)...) + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: fmt.Sprintf("%s-%s-", restoreSession.Name, target.Name), + Namespace: restoreSession.Namespace, + Annotations: map[string]string{ + "targetName": target.Name, + "snapshotId": snapshotId, + }, + }, + Spec: batchv1.JobSpec{ + TTLSecondsAfterFinished: &ttl, + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{restic}, + Containers: []corev1.Container{finalizer}, + Volumes: []corev1.Volume{ + corev1.Volume{Name: "output"}, }, + RestartPolicy: corev1.RestartPolicyOnFailure, }, - } - for _, step := range target.Steps { - function := &formolv1alpha1.Function{} - // get the backup function - if err := r.Get(ctx, client.ObjectKey{ - Namespace: restoreSession.Namespace, - Name: step.Name, - }, function); err != nil { - log.Error(err, "unable to get backup function", "name", step.Name) - return err - } - var restoreName string - if function.Annotations["restoreFunction"] != "" { - restoreName = function.Annotations["restoreFunction"] - } else { - restoreName = strings.Replace(step.Name, "backup", "restore", 1) - } - if err := r.Get(ctx, client.ObjectKey{ - Namespace: restoreSession.Namespace, - Name: restoreName, - }, function); err != nil { - log.Error(err, "unable to get function", "function", step) - return err - } - function.Spec.Name = function.Name - function.Spec.Env = append(step.Env, restoreSessionEnv...) - function.Spec.VolumeMounts = append(function.Spec.VolumeMounts, output) - job.Spec.Template.Spec.InitContainers = append(job.Spec.Template.Spec.InitContainers, function.Spec) - } - if err := ctrl.SetControllerReference(restoreSession, job, r.Scheme); err != nil { - log.Error(err, "unable to set controller on job", "job", job, "restoresession", restoreSession) - return err - } - log.V(0).Info("creating a restore job", "target", target.Name) - if err := r.Create(ctx, job); err != nil { - log.Error(err, "unable to create job", "job", job) - return err - } + }, + }, + } + for _, step := range target.Steps { + function := &formolv1alpha1.Function{} + // get the backup function + if err := r.Get(ctx, client.ObjectKey{ + Namespace: restoreSession.Namespace, + Name: step.Name, + }, function); err != nil { + log.Error(err, "unable to get backup function", "name", step.Name) + return err } + var restoreName string + if function.Annotations["restoreFunction"] != "" { + restoreName = function.Annotations["restoreFunction"] + } else { + restoreName = strings.Replace(step.Name, "backup", "restore", 1) + } + if err := r.Get(ctx, client.ObjectKey{ + Namespace: restoreSession.Namespace, + Name: restoreName, + }, function); err != nil { + log.Error(err, "unable to get function", "function", step) + return err + } + function.Spec.Name = function.Name + function.Spec.Env = append(step.Env, restoreSessionEnv...) + function.Spec.VolumeMounts = append(function.Spec.VolumeMounts, output) + job.Spec.Template.Spec.InitContainers = append(job.Spec.Template.Spec.InitContainers, function.Spec) + } + if err := ctrl.SetControllerReference(restoreSession, job, r.Scheme); err != nil { + log.Error(err, "unable to set controller on job", "job", job, "restoresession", restoreSession) + return err + } + log.V(0).Info("creating a restore job", "target", target.Name) + if err := r.Create(ctx, job); err != nil { + log.Error(err, "unable to create job", "job", job) + return err + //} + //} } return nil } @@ -225,7 +246,7 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err return nil } - createRestoreInitContainer := func(target formolv1alpha1.Target) error { + createRestoreInitContainer := func(target formolv1alpha1.Target, snapshotId string) error { deployment := &appsv1.Deployment{} if err := r.Get(context.Background(), client.ObjectKey{ Namespace: restoreSession.Namespace, @@ -241,51 +262,51 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err return nil } } - for _, targetStatus := range backupSession.Status.Targets { - if targetStatus.Name == target.Name && targetStatus.Kind == target.Kind { - snapshotId := targetStatus.SnapshotId - restoreSessionEnv := []corev1.EnvVar{ - corev1.EnvVar{ - Name: formolv1alpha1.TARGET_NAME, - Value: target.Name, - }, - corev1.EnvVar{ - Name: formolv1alpha1.RESTORESESSION_NAME, - Value: restoreSession.Name, - }, - corev1.EnvVar{ - Name: formolv1alpha1.RESTORESESSION_NAMESPACE, - Value: restoreSession.Namespace, - }, - } - initContainer := corev1.Container{ - Name: RESTORESESSION, - Image: formolutils.FORMOLCLI, - Args: []string{"volume", "restore", "--snapshot-id", snapshotId}, - VolumeMounts: target.VolumeMounts, - Env: restoreSessionEnv, - } - repo := &formolv1alpha1.Repo{} - if err := r.Get(ctx, client.ObjectKey{ - Namespace: backupConf.Namespace, - Name: backupConf.Spec.Repository, - }, repo); err != nil { - log.Error(err, "unable to get Repo from BackupConfiguration") - return err - } - // S3 backing storage - initContainer.Env = append(initContainer.Env, formolutils.ConfigureResticEnvVar(backupConf, repo)...) - deployment.Spec.Template.Spec.InitContainers = append([]corev1.Container{initContainer}, - deployment.Spec.Template.Spec.InitContainers...) - if err := r.Update(ctx, deployment); err != nil { - log.Error(err, "unable to update deployment") - return err - } - - return nil - } + //for _, targetStatus := range backupSession.Status.Targets { + //if targetStatus.Name == target.Name && targetStatus.Kind == target.Kind { + //snapshotId := targetStatus.SnapshotId + restoreSessionEnv := []corev1.EnvVar{ + corev1.EnvVar{ + Name: formolv1alpha1.TARGET_NAME, + Value: target.Name, + }, + corev1.EnvVar{ + Name: formolv1alpha1.RESTORESESSION_NAME, + Value: restoreSession.Name, + }, + corev1.EnvVar{ + Name: formolv1alpha1.RESTORESESSION_NAMESPACE, + Value: restoreSession.Namespace, + }, } + initContainer := corev1.Container{ + Name: RESTORESESSION, + Image: formolutils.FORMOLCLI, + Args: []string{"volume", "restore", "--snapshot-id", snapshotId}, + VolumeMounts: target.VolumeMounts, + Env: restoreSessionEnv, + } + repo := &formolv1alpha1.Repo{} + if err := r.Get(ctx, client.ObjectKey{ + Namespace: backupConf.Namespace, + Name: backupConf.Spec.Repository, + }, repo); err != nil { + log.Error(err, "unable to get Repo from BackupConfiguration") + return err + } + // S3 backing storage + initContainer.Env = append(initContainer.Env, formolutils.ConfigureResticEnvVar(backupConf, repo)...) + deployment.Spec.Template.Spec.InitContainers = append([]corev1.Container{initContainer}, + deployment.Spec.Template.Spec.InitContainers...) + if err := r.Update(ctx, deployment); err != nil { + log.Error(err, "unable to update deployment") + return err + } + return nil + //} + //} + //return nil } startNextTask := func() (*formolv1alpha1.TargetStatus, error) { @@ -301,13 +322,13 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err restoreSession.Status.Targets = append(restoreSession.Status.Targets, targetStatus) switch target.Kind { case formolv1alpha1.SidecarKind: - if err := createRestoreInitContainer(target); err != nil { + if err := createRestoreInitContainer(target, backupSession.Status.Targets[nextTarget].SnapshotId); err != nil { log.V(0).Info("unable to create restore init container", "task", target) targetStatus.SessionState = formolv1alpha1.Failure return nil, err } case formolv1alpha1.JobKind: - if err := createRestoreJob(target); err != nil { + if err := createRestoreJob(target, backupSession.Status.Targets[nextTarget].SnapshotId); err != nil { log.V(0).Info("unable to create restore job", "task", target) targetStatus.SessionState = formolv1alpha1.Failure return nil, err @@ -336,12 +357,12 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err restoreSession.Status.SessionState = formolv1alpha1.Running if targetStatus, err := startNextTask(); err != nil { log.Error(err, "unable to start next restore task") - return ctrl.Result{}, err + return reconcile.Result{}, err } else { log.V(0).Info("New restore. Start the first task", "task", targetStatus.Name) if err := r.Status().Update(ctx, restoreSession); err != nil { log.Error(err, "unable to update restoresession") - return ctrl.Result{}, err + return reconcile.Result{}, err } } case formolv1alpha1.Running: @@ -352,11 +373,11 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err restoreSession.Status.SessionState = formolv1alpha1.Failure if err := r.Status().Update(ctx, restoreSession); err != nil { log.Error(err, "unable to update restoresession") - return ctrl.Result{}, err + return reconcile.Result{}, err } case formolv1alpha1.Running: log.V(0).Info("task is still running", "target", currentTargetStatus.Name) - return ctrl.Result{}, nil + return reconcile.Result{}, nil case formolv1alpha1.Waiting: target := backupConf.Spec.Targets[len(restoreSession.Status.Targets)-1] if target.Kind == formolv1alpha1.SidecarKind { @@ -366,7 +387,7 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err Name: target.Name, }, deployment); err != nil { log.Error(err, "unable to get deployment") - return ctrl.Result{}, err + return reconcile.Result{}, err } if deployment.Status.ReadyReplicas == *deployment.Spec.Replicas { @@ -374,21 +395,21 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err currentTargetStatus.SessionState = formolv1alpha1.Finalize if err := r.Status().Update(ctx, restoreSession); err != nil { log.Error(err, "unable to update restoresession") - return ctrl.Result{}, err + return reconcile.Result{}, err } } else { log.V(0).Info("Waiting for the sidecar to come back") - return ctrl.Result{RequeueAfter: 10 * time.Second}, nil + return reconcile.Result{RequeueAfter: 10 * time.Second}, nil } } else { log.V(0).Info("not a SidecarKind. Ignoring Waiting") } case formolv1alpha1.Success: _ = endTask() - log.V(0).Info("last task was a success. start a new one", "target", currentTargetStatus) + log.V(0).Info("last task was a success. start a new one", "target", currentTargetStatus, "restoreSession version", restoreSession.ObjectMeta.ResourceVersion) targetStatus, err := startNextTask() if err != nil { - return ctrl.Result{}, err + return reconcile.Result{}, err } if targetStatus == nil { // No more task to start. The restore is over @@ -396,7 +417,7 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err } if err := r.Status().Update(ctx, restoreSession); err != nil { log.Error(err, "unable to update restoresession") - return ctrl.Result{}, err + return reconcile.Result{RequeueAfter: 300 * time.Millisecond}, nil } } case "": @@ -405,13 +426,26 @@ func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, err restoreSession.Status.StartTime = &metav1.Time{Time: time.Now()} if err := r.Status().Update(ctx, restoreSession); err != nil { log.Error(err, "unable to update restoreSession") - return ctrl.Result{}, err + return reconcile.Result{}, err } } - return ctrl.Result{}, nil + return reconcile.Result{}, nil } func (r *RestoreSessionReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &batchv1.Job{}, jobOwnerKey, func(rawObj client.Object) []string { + job := rawObj.(*batchv1.Job) + owner := metav1.GetControllerOf(job) + if owner == nil { + return nil + } + if owner.APIVersion != formolv1alpha1.GroupVersion.String() || owner.Kind != "RestoreSession" { + return nil + } + return []string{owner.Name} + }); err != nil { + return err + } return ctrl.NewControllerManagedBy(mgr). For(&formolv1alpha1.RestoreSession{}). Owns(&batchv1.Job{}). diff --git a/go.mod b/go.mod index 9113c58..b26e686 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,16 @@ module github.com/desmo999r/formol go 1.13 require ( - github.com/go-logr/logr v0.1.0 - github.com/onsi/ginkgo v1.12.1 - github.com/onsi/gomega v1.10.1 + github.com/go-logr/logr v0.3.0 + github.com/gophercloud/gophercloud v0.1.0 // indirect + github.com/onsi/ginkgo v1.14.1 + github.com/onsi/gomega v1.10.2 gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e // indirect - k8s.io/api v0.18.6 - k8s.io/apimachinery v0.18.6 - k8s.io/client-go v0.18.6 - sigs.k8s.io/controller-runtime v0.6.4 + k8s.io/api v0.20.2 + k8s.io/apimachinery v0.20.2 + k8s.io/client-go v0.20.2 + k8s.io/klog v1.0.0 // indirect + sigs.k8s.io/controller-runtime v0.8.3 sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 // indirect + sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect )