Skip to content

Commit 1cd06ee

Browse files
snecklifterclaude
andauthored
fix(backup): use configured imagePullPolicy for backup CronJob (#1654)
The backup CronJob controller hardcodes imagePullPolicy to "Always" on backup Job containers. This does not respect the imagePullPolicy setting from DevWorkspaceOperatorConfig, unlike other workspace-related pods. Read imagePullPolicy from the operator config, falling back to "Always" when the field is not explicitly set. Fixes: #1637 Assisted-by: Claude Code Signed-off-by: Chris Brown <chribrow@redhat.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 686bc5f commit 1cd06ee

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

controllers/backupcronjob/backupcronjob_controller.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ func (r *BackupCronJobReconciler) createBackupJob(
415415
{Name: "ORAS_EXTRA_ARGS", Value: orasExtraArgs},
416416
},
417417
Image: images.GetProjectBackupImage(),
418-
ImagePullPolicy: "Always",
418+
ImagePullPolicy: getImagePullPolicy(dwOperatorConfig),
419419
Args: []string{
420420
"/workspace-recovery.sh",
421421
"--backup",
@@ -486,3 +486,10 @@ func (r *BackupCronJobReconciler) createBackupJob(
486486
log.Info("Created backup Job for DevWorkspace", "jobName", job.Name, "devworkspace", workspace.Name)
487487
return nil
488488
}
489+
490+
func getImagePullPolicy(dwOperatorConfig *controllerv1alpha1.DevWorkspaceOperatorConfig) corev1.PullPolicy {
491+
if dwOperatorConfig.Config.Workspace.ImagePullPolicy != "" {
492+
return corev1.PullPolicy(dwOperatorConfig.Config.Workspace.ImagePullPolicy)
493+
}
494+
return corev1.PullAlways
495+
}

controllers/backupcronjob/backupcronjob_controller_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,75 @@ var _ = Describe("BackupCronJobReconciler", func() {
426426
Expect(*jobList.Items[0].Spec.BackoffLimit).To(Equal(int32(2)))
427427
})
428428

429+
It("creates a Job with configured imagePullPolicy", 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+
ImagePullPolicy: "IfNotPresent",
437+
BackupCronJob: &controllerv1alpha1.BackupCronJobConfig{
438+
Enable: &enabled,
439+
Schedule: schedule,
440+
Registry: &controllerv1alpha1.RegistryConfig{
441+
Path: "fake-registry",
442+
},
443+
},
444+
},
445+
},
446+
}
447+
Expect(fakeClient.Create(ctx, dwoc)).To(Succeed())
448+
dw := createDevWorkspace("dw-pullpolicy", "ns-a", false, metav1.NewTime(time.Now().Add(-10*time.Minute)))
449+
dw.Status.Phase = dwv2.DevWorkspaceStatusStopped
450+
dw.Status.DevWorkspaceId = "id-pullpolicy"
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+
Expect(reconciler.executeBackupSync(ctx, dwoc, log)).To(Succeed())
457+
458+
jobList := &batchv1.JobList{}
459+
Expect(fakeClient.List(ctx, jobList, &client.ListOptions{Namespace: dw.Namespace})).To(Succeed())
460+
Expect(jobList.Items).To(HaveLen(1))
461+
Expect(jobList.Items[0].Spec.Template.Spec.Containers[0].ImagePullPolicy).To(Equal(corev1.PullIfNotPresent))
462+
})
463+
464+
It("defaults imagePullPolicy to Always when not configured", func() {
465+
enabled := true
466+
schedule := "* * * * *"
467+
dwoc := &controllerv1alpha1.DevWorkspaceOperatorConfig{
468+
ObjectMeta: metav1.ObjectMeta{Name: nameNamespace.Name, Namespace: nameNamespace.Namespace},
469+
Config: &controllerv1alpha1.OperatorConfiguration{
470+
Workspace: &controllerv1alpha1.WorkspaceConfig{
471+
BackupCronJob: &controllerv1alpha1.BackupCronJobConfig{
472+
Enable: &enabled,
473+
Schedule: schedule,
474+
Registry: &controllerv1alpha1.RegistryConfig{
475+
Path: "fake-registry",
476+
},
477+
},
478+
},
479+
},
480+
}
481+
Expect(fakeClient.Create(ctx, dwoc)).To(Succeed())
482+
dw := createDevWorkspace("dw-default-policy", "ns-a", false, metav1.NewTime(time.Now().Add(-10*time.Minute)))
483+
dw.Status.Phase = dwv2.DevWorkspaceStatusStopped
484+
dw.Status.DevWorkspaceId = "id-default-policy"
485+
Expect(fakeClient.Create(ctx, dw)).To(Succeed())
486+
487+
pvc := &corev1.PersistentVolumeClaim{ObjectMeta: metav1.ObjectMeta{Name: "claim-devworkspace", Namespace: dw.Namespace}}
488+
Expect(fakeClient.Create(ctx, pvc)).To(Succeed())
489+
490+
Expect(reconciler.executeBackupSync(ctx, dwoc, log)).To(Succeed())
491+
492+
jobList := &batchv1.JobList{}
493+
Expect(fakeClient.List(ctx, jobList, &client.ListOptions{Namespace: dw.Namespace})).To(Succeed())
494+
Expect(jobList.Items).To(HaveLen(1))
495+
Expect(jobList.Items[0].Spec.Template.Spec.Containers[0].ImagePullPolicy).To(Equal(corev1.PullAlways))
496+
})
497+
429498
It("does not create a Job when the DevWorkspace was stopped beyond time range", func() {
430499
enabled := true
431500
schedule := "* * * * *"

0 commit comments

Comments
 (0)