@@ -426,6 +426,116 @@ var _ = Describe("BackupCronJobReconciler", func() {
426426 Expect (* jobList .Items [0 ].Spec .BackoffLimit ).To (Equal (int32 (2 )))
427427 })
428428
429+ It ("creates a Job with node affinity when a running pod mounts the PVC" , func () {
430+ enabled := true
431+ schedule := "* * * * *"
432+ dwoc := & controllerv1alpha1.DevWorkspaceOperatorConfig {
433+ ObjectMeta : metav1.ObjectMeta {Name : nameNamespace .Name , Namespace : nameNamespace .Namespace },
434+ Config : & controllerv1alpha1.OperatorConfiguration {
435+ Workspace : & controllerv1alpha1.WorkspaceConfig {
436+ BackupCronJob : & controllerv1alpha1.BackupCronJobConfig {
437+ Enable : & enabled ,
438+ Schedule : schedule ,
439+ Registry : & controllerv1alpha1.RegistryConfig {
440+ Path : "fake-registry" ,
441+ AuthSecret : "backup-auth" ,
442+ },
443+ },
444+ },
445+ },
446+ }
447+ Expect (fakeClient .Create (ctx , dwoc )).To (Succeed ())
448+ dw := createDevWorkspace ("dw-affinity" , "ns-affinity" , false , metav1 .NewTime (time .Now ().Add (- 10 * time .Minute )))
449+ dw .Status .Phase = dwv2 .DevWorkspaceStatusStopped
450+ dw .Status .DevWorkspaceId = "id-affinity"
451+ Expect (fakeClient .Create (ctx , dw )).To (Succeed ())
452+
453+ pvc := & corev1.PersistentVolumeClaim {ObjectMeta : metav1.ObjectMeta {Name : "claim-devworkspace" , Namespace : dw .Namespace }}
454+ Expect (fakeClient .Create (ctx , pvc )).To (Succeed ())
455+
456+ authSecret := createAuthSecret ("backup-auth" , nameNamespace .Namespace , map [string ][]byte {})
457+ Expect (fakeClient .Create (ctx , authSecret )).To (Succeed ())
458+
459+ // Create a running pod that mounts the PVC on a specific node
460+ pod := & corev1.Pod {
461+ ObjectMeta : metav1.ObjectMeta {
462+ Name : "workspace-pod" ,
463+ Namespace : dw .Namespace ,
464+ Labels : map [string ]string {constants .DevWorkspaceIDLabel : "other-workspace-id" },
465+ },
466+ Spec : corev1.PodSpec {
467+ NodeName : "worker-node-1" ,
468+ Volumes : []corev1.Volume {
469+ {
470+ Name : "workspace-data" ,
471+ VolumeSource : corev1.VolumeSource {
472+ PersistentVolumeClaim : & corev1.PersistentVolumeClaimVolumeSource {
473+ ClaimName : "claim-devworkspace" ,
474+ },
475+ },
476+ },
477+ },
478+ },
479+ Status : corev1.PodStatus {Phase : corev1 .PodRunning },
480+ }
481+ Expect (fakeClient .Create (ctx , pod )).To (Succeed ())
482+
483+ Expect (reconciler .executeBackupSync (ctx , dwoc , log )).To (Succeed ())
484+
485+ jobList := & batchv1.JobList {}
486+ Expect (fakeClient .List (ctx , jobList , & client.ListOptions {Namespace : dw .Namespace })).To (Succeed ())
487+ Expect (jobList .Items ).To (HaveLen (1 ))
488+ job := jobList .Items [0 ]
489+ Expect (job .Spec .Template .Spec .Affinity ).ToNot (BeNil ())
490+ Expect (job .Spec .Template .Spec .Affinity .NodeAffinity ).ToNot (BeNil ())
491+ nodeSelector := job .Spec .Template .Spec .Affinity .NodeAffinity .RequiredDuringSchedulingIgnoredDuringExecution
492+ Expect (nodeSelector ).ToNot (BeNil ())
493+ Expect (nodeSelector .NodeSelectorTerms ).To (HaveLen (1 ))
494+ Expect (nodeSelector .NodeSelectorTerms [0 ].MatchExpressions ).To (HaveLen (1 ))
495+ expr := nodeSelector .NodeSelectorTerms [0 ].MatchExpressions [0 ]
496+ Expect (expr .Key ).To (Equal ("kubernetes.io/hostname" ))
497+ Expect (expr .Operator ).To (Equal (corev1 .NodeSelectorOpIn ))
498+ Expect (expr .Values ).To (Equal ([]string {"worker-node-1" }))
499+ })
500+
501+ It ("creates a Job without node affinity when no running pod mounts the PVC" , func () {
502+ enabled := true
503+ schedule := "* * * * *"
504+ dwoc := & controllerv1alpha1.DevWorkspaceOperatorConfig {
505+ ObjectMeta : metav1.ObjectMeta {Name : nameNamespace .Name , Namespace : nameNamespace .Namespace },
506+ Config : & controllerv1alpha1.OperatorConfiguration {
507+ Workspace : & controllerv1alpha1.WorkspaceConfig {
508+ BackupCronJob : & controllerv1alpha1.BackupCronJobConfig {
509+ Enable : & enabled ,
510+ Schedule : schedule ,
511+ Registry : & controllerv1alpha1.RegistryConfig {
512+ Path : "fake-registry" ,
513+ AuthSecret : "backup-auth" ,
514+ },
515+ },
516+ },
517+ },
518+ }
519+ Expect (fakeClient .Create (ctx , dwoc )).To (Succeed ())
520+ dw := createDevWorkspace ("dw-no-affinity" , "ns-no-affinity" , false , metav1 .NewTime (time .Now ().Add (- 10 * time .Minute )))
521+ dw .Status .Phase = dwv2 .DevWorkspaceStatusStopped
522+ dw .Status .DevWorkspaceId = "id-no-affinity"
523+ Expect (fakeClient .Create (ctx , dw )).To (Succeed ())
524+
525+ pvc := & corev1.PersistentVolumeClaim {ObjectMeta : metav1.ObjectMeta {Name : "claim-devworkspace" , Namespace : dw .Namespace }}
526+ Expect (fakeClient .Create (ctx , pvc )).To (Succeed ())
527+
528+ authSecret := createAuthSecret ("backup-auth" , nameNamespace .Namespace , map [string ][]byte {})
529+ Expect (fakeClient .Create (ctx , authSecret )).To (Succeed ())
530+
531+ Expect (reconciler .executeBackupSync (ctx , dwoc , log )).To (Succeed ())
532+
533+ jobList := & batchv1.JobList {}
534+ Expect (fakeClient .List (ctx , jobList , & client.ListOptions {Namespace : dw .Namespace })).To (Succeed ())
535+ Expect (jobList .Items ).To (HaveLen (1 ))
536+ Expect (jobList .Items [0 ].Spec .Template .Spec .Affinity ).To (BeNil ())
537+ })
538+
429539 It ("creates a Job with configured podSecurityContext" , func () {
430540 enabled := true
431541 schedule := "* * * * *"
0 commit comments