@@ -18,6 +18,7 @@ package controllers
1818import (
1919 "context"
2020
21+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2122 "sigs.k8s.io/controller-runtime/pkg/reconcile"
2223
2324 dw "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
@@ -163,8 +164,7 @@ func (r *BackupCronJobReconciler) Reconcile(ctx context.Context, req ctrl.Reques
163164 return ctrl.Result {}, nil
164165 }
165166
166- backUpConfig := dwOperatorConfig .Config .Workspace .BackupCronJob
167- r .startCron (ctx , backUpConfig , log )
167+ r .startCron (ctx , dwOperatorConfig , log )
168168
169169 return ctrl.Result {}, nil
170170}
@@ -180,7 +180,7 @@ func (r *BackupCronJobReconciler) isBackupEnabled(config *controllerv1alpha1.Dev
180180}
181181
182182// startCron starts the cron scheduler with the backup job according to the provided configuration.
183- func (r * BackupCronJobReconciler ) startCron (ctx context.Context , req ctrl. Request , backUpConfig * controllerv1alpha1.BackupCronJobConfig , logger logr.Logger ) {
183+ func (r * BackupCronJobReconciler ) startCron (ctx context.Context , dwOperatorConfig * controllerv1alpha1.DevWorkspaceOperatorConfig , logger logr.Logger ) {
184184 log := logger .WithName ("backup cron" )
185185 log .Info ("Starting backup cron scheduler" )
186186
@@ -193,12 +193,13 @@ func (r *BackupCronJobReconciler) startCron(ctx context.Context, req ctrl.Reques
193193 }
194194
195195 // add cronjob task
196+ backUpConfig := dwOperatorConfig .Config .Workspace .BackupCronJob
196197 log .Info ("Adding cronjob task" , "schedule" , backUpConfig .Schedule )
197198 _ , err := r .cron .AddFunc (backUpConfig .Schedule , func () {
198199 taskLog := logger .WithName ("cronTask" )
199200
200201 taskLog .Info ("Starting DevWorkspace backup job" )
201- if err := r .executeBackupSync (ctx , req , backUpConfig , logger ); err != nil {
202+ if err := r .executeBackupSync (ctx , dwOperatorConfig , logger ); err != nil {
202203 taskLog .Error (err , "Failed to execute backup job for DevWorkspaces" )
203204 }
204205 taskLog .Info ("DevWorkspace backup job finished" )
@@ -224,47 +225,63 @@ func (r *BackupCronJobReconciler) stopCron(logger logr.Logger) {
224225 }
225226
226227 ctx := r .cron .Stop ()
227- ctx .Done ()
228+ <- ctx .Done ()
228229
229230 log .Info ("Cron scheduler stopped" )
230231}
231232
232233// executeBackupSync executes the backup job for all DevWorkspaces in the cluster that
233234// have been stopped in the last N minutes.
234- func (r * BackupCronJobReconciler ) executeBackupSync (ctx context.Context , req ctrl. Request , backUpConfig * controllerv1alpha1.BackupCronJobConfig , logger logr.Logger ) error {
235+ func (r * BackupCronJobReconciler ) executeBackupSync (ctx context.Context , dwOperatorConfig * controllerv1alpha1.DevWorkspaceOperatorConfig , logger logr.Logger ) error {
235236 log := logger .WithName ("executeBackupSync" )
236237 log .Info ("Executing backup sync for all DevWorkspaces" )
238+ backUpConfig := dwOperatorConfig .Config .Workspace .BackupCronJob
237239 devWorkspaces := & dw.DevWorkspaceList {}
238240 err := r .List (ctx , devWorkspaces )
239241 if err != nil {
240242 log .Error (err , "Failed to list DevWorkspaces" )
241243 return err
242244 }
245+ var lastBackupTime * metav1.Time
246+ if dwOperatorConfig .Status != nil && dwOperatorConfig .Status .LastBackupTime != nil {
247+ lastBackupTime = dwOperatorConfig .Status .LastBackupTime
248+ }
243249 for _ , dw := range devWorkspaces .Items {
244- if ! r .wasStoppedInTimeRange (& dw , 30 , ctx , logger ) {
250+ if ! r .wasStoppedSinceLastBackup (& dw , lastBackupTime , logger ) {
245251 log .Info ("Skipping backup for DevWorkspace that wasn't stopped recently" , "namespace" , dw .Namespace , "name" , dw .Name )
246252 continue
247253 }
248254 dwID := dw .Status .DevWorkspaceId
249255 log .Info ("Found DevWorkspace" , "namespace" , dw .Namespace , "devworkspace" , dw .Name , "id" , dwID )
250256
251- if err := r .createBackupJob (& dw , ctx , req , backUpConfig , logger ); err != nil {
257+ if err := r .createBackupJob (& dw , ctx , backUpConfig , logger ); err != nil {
252258 log .Error (err , "Failed to create backup Job for DevWorkspace" , "id" , dwID )
253259 continue
254260 }
255261 log .Info ("Backup Job created for DevWorkspace" , "id" , dwID )
256262
257263 }
264+ origConfig := client .MergeFrom (dwOperatorConfig .DeepCopy ())
265+ if dwOperatorConfig .Status == nil {
266+ dwOperatorConfig .Status = & controllerv1alpha1.OperatorConfigurationStatus {}
267+ }
268+ dwOperatorConfig .Status .LastBackupTime = & metav1.Time {Time : metav1 .Now ().Time }
269+
270+ err = r .Status ().Patch (ctx , dwOperatorConfig , origConfig )
271+ if err != nil {
272+ log .Error (err , "Failed to update DevWorkspaceOperatorConfig status with last backup time" )
273+ // Not returning error as the backup jobs were created successfully
274+ }
258275 return nil
259276}
260277
261- // wasStoppedInTimeRange checks if the DevWorkspace was stopped in the last N minutes .
262- func (r * BackupCronJobReconciler ) wasStoppedInTimeRange (workspace * dw.DevWorkspace , timeRangeInMinute float64 , ctx context. Context , logger logr.Logger ) bool {
263- log := logger .WithName ("wasStoppedInTimeRange " )
278+ // wasStoppedSinceLastBackup checks if the DevWorkspace was stopped since the last backup time .
279+ func (r * BackupCronJobReconciler ) wasStoppedSinceLastBackup (workspace * dw.DevWorkspace , lastBackupTime * metav1. Time , logger logr.Logger ) bool {
280+ log := logger .WithName ("wasStoppedSinceLastBackup " )
264281 if workspace .Status .Phase != dw .DevWorkspaceStatusStopped {
265282 return false
266283 }
267- log .Info ("DevWorkspace is currently stopped, checking if it was stopped recently " , "namespace" , workspace .Namespace , "name" , workspace .Name )
284+ log .Info ("DevWorkspace is currently stopped, checking if it was stopped since last backup " , "namespace" , workspace .Namespace , "name" , workspace .Name )
268285 // Check if the workspace was stopped in the last N minutes
269286 if workspace .Status .Conditions != nil {
270287 lastTimeStopped := metav1.Time {}
@@ -273,11 +290,13 @@ func (r *BackupCronJobReconciler) wasStoppedInTimeRange(workspace *dw.DevWorkspa
273290 lastTimeStopped = condition .LastTransitionTime
274291 }
275292 }
276- // Calculate the time difference
277293 if ! lastTimeStopped .IsZero () {
278- timeDiff := metav1 .Now ().Sub (lastTimeStopped .Time )
279- if timeDiff .Minutes () <= timeRangeInMinute {
280- log .Info ("DevWorkspace was stopped recently" , "namespace" , workspace .Namespace , "name" , workspace .Name )
294+ if lastBackupTime == nil {
295+ // No previous backup, so consider it stopped since last backup
296+ return true
297+ }
298+ if lastTimeStopped .Time .After (lastBackupTime .Time ) {
299+ log .Info ("DevWorkspace was stopped since last backup" , "namespace" , workspace .Namespace , "name" , workspace .Name )
281300 return true
282301 }
283302 }
@@ -290,7 +309,7 @@ func ptrInt32(i int32) *int32 { return &i }
290309func ptrBool (b bool ) * bool { return & b }
291310
292311// createBackupJob creates a Kubernetes Job to back up the workspace's PVC data.
293- func (r * BackupCronJobReconciler ) createBackupJob (workspace * dw.DevWorkspace , ctx context.Context , req ctrl. Request , backUpConfig * controllerv1alpha1.BackupCronJobConfig , logger logr.Logger ) error {
312+ func (r * BackupCronJobReconciler ) createBackupJob (workspace * dw.DevWorkspace , ctx context.Context , backUpConfig * controllerv1alpha1.BackupCronJobConfig , logger logr.Logger ) error {
294313 log := logger .WithName ("createBackupJob" )
295314 dwID := workspace .Status .DevWorkspaceId
296315
@@ -304,8 +323,12 @@ func (r *BackupCronJobReconciler) createBackupJob(workspace *dw.DevWorkspace, ct
304323
305324 job := & batchv1.Job {
306325 ObjectMeta : metav1.ObjectMeta {
307- GenerateName : "backup-job-" ,
326+ GenerateName : constants . DevWorkspaceBackupJobNamePrefix ,
308327 Namespace : workspace .Namespace ,
328+ Labels : map [string ]string {
329+ constants .DevWorkspaceIDLabel : dwID ,
330+ constants .DevWorkspaceBackupJobLabel : "true" ,
331+ },
309332 },
310333 Spec : batchv1.JobSpec {
311334 Template : corev1.PodTemplateSpec {
@@ -375,6 +398,9 @@ func (r *BackupCronJobReconciler) createBackupJob(workspace *dw.DevWorkspace, ct
375398 },
376399 },
377400 }
401+ if err := controllerutil .SetControllerReference (workspace , job , r .Scheme ); err != nil {
402+ return err
403+ }
378404 err = r .Create (ctx , job )
379405 if err != nil {
380406 log .Error (err , "Failed to create backup Job for DevWorkspace" , "devworkspace" , workspace .Name )
0 commit comments