Skip to content

Commit 8ebae9e

Browse files
committed
Changes job handling in gitopts controller
Changes the job handling so jobs are deleted after they succeed. Jobs are created when they are needed, this avoids having always a minimum of 1 job per `gitrepo` resource. It also deletes the Owns(job) statement from the reconciler setup as we're already setting the controller reference when creating the job and the Owns statement makes extra reconciler calls that are not needed. Signed-off-by: Xavi Garcia <[email protected]>
1 parent 54f46c8 commit 8ebae9e

File tree

2 files changed

+112
-33
lines changed

2 files changed

+112
-33
lines changed

integrationtests/gitjob/controller/controller_test.go

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"k8s.io/client-go/util/retry"
2222
"sigs.k8s.io/controller-runtime/pkg/client"
2323

24+
"github.com/rancher/wrangler/pkg/name"
2425
"github.com/rancher/wrangler/v3/pkg/genericcondition"
2526

2627
"github.com/rancher/fleet/integrationtests/utils"
@@ -183,7 +184,7 @@ var _ = Describe("GitJob controller", func() {
183184
TypeMeta: metav1.TypeMeta{Kind: "GitRepo"},
184185
})
185186
g.Expect(events).ToNot(BeNil())
186-
g.Expect(len(events.Items)).To(Equal(2))
187+
g.Expect(len(events.Items)).To(Equal(3))
187188
g.Expect(events.Items[0].Reason).To(Equal("GotNewCommit"))
188189
g.Expect(events.Items[0].Message).To(Equal("9ca3a0ad308ed8bffa6602572e2a1343af9c3d2e"))
189190
g.Expect(events.Items[0].Type).To(Equal("Normal"))
@@ -192,7 +193,17 @@ var _ = Describe("GitJob controller", func() {
192193
g.Expect(events.Items[1].Message).To(Equal("GitJob was created"))
193194
g.Expect(events.Items[1].Type).To(Equal("Normal"))
194195
g.Expect(events.Items[1].Source.Component).To(Equal("gitjob-controller"))
196+
g.Expect(events.Items[2].Reason).To(Equal("JobDeleted"))
197+
g.Expect(events.Items[2].Message).To(Equal("job deletion triggered because job succedded"))
198+
g.Expect(events.Items[2].Type).To(Equal("Normal"))
199+
g.Expect(events.Items[2].Source.Component).To(Equal("gitjob-controller"))
195200
}).Should(Succeed())
201+
202+
// job should not be present
203+
Consistently(func() bool {
204+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
205+
return errors.IsNotFound(err)
206+
}, 10*time.Second, 1*time.Second).Should(BeTrue())
196207
})
197208
})
198209

@@ -312,13 +323,6 @@ var _ = Describe("GitJob controller", func() {
312323
g.Expect(checkCondition(&gitRepo, "Ready", corev1.ConditionTrue, "")).To(BeTrue())
313324
g.Expect(checkCondition(&gitRepo, "Accepted", corev1.ConditionTrue, "")).To(BeTrue())
314325
}).Should(Succeed())
315-
316-
By("verifying that the job is deleted if Spec.Generation changed")
317-
Expect(simulateIncreaseGitRepoGeneration(gitRepo)).ToNot(HaveOccurred())
318-
Eventually(func() bool {
319-
jobName = names.SafeConcatName(gitRepoName, names.Hex(repo+commit, 5))
320-
return errors.IsNotFound(k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job))
321-
}).Should(BeTrue())
322326
})
323327
})
324328
})
@@ -373,9 +377,51 @@ var _ = Describe("GitJob controller", func() {
373377
jobName = names.SafeConcatName(gitRepoName, names.Hex(repo+commit, 5))
374378
return k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
375379
}).Should(Not(HaveOccurred()))
380+
// simulate job was successful
381+
Eventually(func() error {
382+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
383+
// We could be checking this when the job is still not created
384+
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
385+
job.Status.Succeeded = 1
386+
job.Status.Conditions = []batchv1.JobCondition{
387+
{
388+
Type: "Complete",
389+
Status: "True",
390+
},
391+
}
392+
return k8sClient.Status().Update(ctx, &job)
393+
}).Should(Not(HaveOccurred()))
394+
// wait until the job has finished
395+
Eventually(func() bool {
396+
jobName = name.SafeConcatName(gitRepoName, name.Hex(repo+commit, 5))
397+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
398+
return errors.IsNotFound(err)
399+
}).Should(BeTrue())
400+
376401
// store the generation value to compare against later
377402
generationValue = gitRepo.Spec.ForceSyncGeneration
378403
Expect(simulateIncreaseForceSyncGeneration(gitRepo)).ToNot(HaveOccurred())
404+
// simulate job was successful
405+
Eventually(func() error {
406+
jobName = name.SafeConcatName(gitRepoName, name.Hex(repo+commit, 5))
407+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
408+
// We could be checking this when the job is still not created
409+
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
410+
job.Status.Succeeded = 1
411+
job.Status.Conditions = []batchv1.JobCondition{
412+
{
413+
Type: "Complete",
414+
Status: "True",
415+
},
416+
}
417+
return k8sClient.Status().Update(ctx, &job)
418+
}).Should(Not(HaveOccurred()))
419+
// wait until the job has finished
420+
Eventually(func() bool {
421+
jobName = name.SafeConcatName(gitRepoName, name.Hex(repo+commit, 5))
422+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
423+
return errors.IsNotFound(err)
424+
}).Should(BeTrue())
379425
})
380426
BeforeEach(func() {
381427
expectedCommit = commit
@@ -414,7 +460,7 @@ var _ = Describe("GitJob controller", func() {
414460
g.Expect(events.Items[1].Type).To(Equal("Normal"))
415461
g.Expect(events.Items[1].Source.Component).To(Equal("gitjob-controller"))
416462
g.Expect(events.Items[2].Reason).To(Equal("JobDeleted"))
417-
g.Expect(events.Items[2].Message).To(Equal("job deletion triggered because of ForceUpdateGeneration"))
463+
g.Expect(events.Items[2].Message).To(Equal("job deletion force-deletion-fa0eb triggered because job succedded"))
418464
g.Expect(events.Items[2].Type).To(Equal("Normal"))
419465
g.Expect(events.Items[2].Source.Component).To(Equal("gitjob-controller"))
420466
}).Should(Succeed())
@@ -446,6 +492,26 @@ var _ = Describe("GitJob controller", func() {
446492
jobName = names.SafeConcatName(gitRepoName, names.Hex(repo+commit, 5))
447493
return k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
448494
}).Should(Not(HaveOccurred()))
495+
// simulate job was successful
496+
Eventually(func() error {
497+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
498+
// We could be checking this when the job is still not created
499+
Expect(client.IgnoreNotFound(err)).ToNot(HaveOccurred())
500+
job.Status.Succeeded = 1
501+
job.Status.Conditions = []batchv1.JobCondition{
502+
{
503+
Type: "Complete",
504+
Status: "True",
505+
},
506+
}
507+
return k8sClient.Status().Update(ctx, &job)
508+
}).Should(Not(HaveOccurred()))
509+
// wait until the job has finished
510+
Eventually(func() bool {
511+
jobName = name.SafeConcatName(gitRepoName, name.Hex(repo+commit, 5))
512+
err := k8sClient.Get(ctx, types.NamespacedName{Name: jobName, Namespace: gitRepoNamespace}, &job)
513+
return errors.IsNotFound(err)
514+
}).Should(BeTrue())
449515

450516
// change a gitrepo field, this will change the Generation field. This simulates changing fleet apply parameters.
451517
Expect(retry.RetryOnConflict(retry.DefaultRetry, func() error {

internal/cmd/controller/gitops/reconciler/gitjob_controller.go

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ func (r *GitJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
102102
),
103103
),
104104
).
105-
Owns(&batchv1.Job{}).
106105
Watches(
107106
// Fan out from bundle to gitrepo
108107
&v1alpha1.Bundle{},
@@ -261,7 +260,8 @@ func (r *GitJobReconciler) manageGitJob(ctx context.Context, logger logr.Logger,
261260
}
262261
}
263262

264-
if gitrepo.Status.Commit != "" {
263+
if r.shouldCreateJob(gitrepo, oldCommit) {
264+
r.updateGenerationValuesIfNeeded(gitrepo)
265265
if err := r.validateExternalSecretExist(ctx, gitrepo); err != nil {
266266
r.Recorder.Event(gitrepo, fleetevent.Warning, "FailedValidatingSecret", err.Error())
267267
return result(repoPolled, gitrepo), grutil.UpdateErrorStatus(ctx, r.Client, name, gitrepo.Status, err)
@@ -281,7 +281,7 @@ func (r *GitJobReconciler) manageGitJob(ctx context.Context, logger logr.Logger,
281281
}
282282
r.Recorder.Event(gitrepo, fleetevent.Normal, "Created", "GitJob was created")
283283
}
284-
} else if gitrepo.Status.Commit != "" {
284+
} else if gitrepo.Status.Commit != "" && gitrepo.Status.Commit == oldCommit {
285285
if err = r.deleteJobIfNeeded(ctx, gitrepo, &job); err != nil {
286286
return result(repoPolled, gitrepo), fmt.Errorf("error deleting git job: %w", err)
287287
}
@@ -330,6 +330,37 @@ func (r *GitJobReconciler) cleanupGitRepo(ctx context.Context, logger logr.Logge
330330
return nil
331331
}
332332

333+
// shouldCreateJob checks if the conditions to create a new job are met.
334+
// It checks for all the conditions so, in case more than one is met, it sets all the
335+
// values related in one single reconciler loop
336+
func (r *GitJobReconciler) shouldCreateJob(gitrepo *v1alpha1.GitRepo, oldCommit string) bool {
337+
if gitrepo.Status.Commit != "" && gitrepo.Status.Commit != oldCommit {
338+
return true
339+
}
340+
341+
if gitrepo.Spec.ForceSyncGeneration != gitrepo.Status.UpdateGeneration {
342+
return true
343+
}
344+
345+
// k8s Jobs are immutable. Recreate the job if the GitRepo Spec has changed.
346+
// Avoid deleting the job twice
347+
if generationChanged(gitrepo) {
348+
return true
349+
}
350+
351+
return false
352+
}
353+
354+
func (r *GitJobReconciler) updateGenerationValuesIfNeeded(gitrepo *v1alpha1.GitRepo) {
355+
if gitrepo.Spec.ForceSyncGeneration != gitrepo.Status.UpdateGeneration {
356+
gitrepo.Status.UpdateGeneration = gitrepo.Spec.ForceSyncGeneration
357+
}
358+
359+
if generationChanged(gitrepo) {
360+
gitrepo.Status.ObservedGeneration = gitrepo.Generation
361+
}
362+
}
363+
333364
func (r *GitJobReconciler) addGitRepoFinalizer(ctx context.Context, nsName types.NamespacedName) error {
334365
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
335366
gitrepo := &v1alpha1.GitRepo{}
@@ -444,32 +475,14 @@ func (r *GitJobReconciler) createJob(ctx context.Context, gitRepo *v1alpha1.GitR
444475

445476
func (r *GitJobReconciler) deleteJobIfNeeded(ctx context.Context, gitRepo *v1alpha1.GitRepo, job *batchv1.Job) error {
446477
logger := log.FromContext(ctx)
447-
jobDeleted := false
448-
jobDeletedMessage := ""
449-
// if force delete is set, delete the job to make sure a new job is created
450-
if gitRepo.Spec.ForceSyncGeneration != gitRepo.Status.UpdateGeneration {
451-
gitRepo.Status.UpdateGeneration = gitRepo.Spec.ForceSyncGeneration
452-
jobDeletedMessage = "job deletion triggered because of ForceUpdateGeneration"
453-
logger.Info(jobDeletedMessage)
454-
if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil && !errors.IsNotFound(err) {
455-
return err
456-
}
457-
jobDeleted = true
458-
}
459478

460-
// k8s Jobs are immutable. Recreate the job if the GitRepo Spec has changed.
461-
// Avoid deleting the job twice
462-
if !jobDeleted && generationChanged(gitRepo) {
463-
jobDeletedMessage = "job deletion triggered because of generation change"
464-
gitRepo.Status.ObservedGeneration = gitRepo.Generation
479+
// check if the job finished and was successful
480+
if job.Status.Succeeded == 1 {
481+
jobDeletedMessage := fmt.Sprintf("job deletion %s triggered because job succedded", job.Name)
465482
logger.Info(jobDeletedMessage)
466483
if err := r.Delete(ctx, job, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil && !errors.IsNotFound(err) {
467484
return err
468485
}
469-
jobDeleted = true
470-
}
471-
472-
if jobDeleted {
473486
r.Recorder.Event(gitRepo, fleetevent.Normal, "JobDeleted", jobDeletedMessage)
474487
}
475488

0 commit comments

Comments
 (0)