@@ -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