Skip to content

Commit 76245fa

Browse files
committed
Replace Event error with Generic error in GitRepo
For gradual migration to Generic error, update only the GitRepo reconciler to use Generic error. Replace the Waiting error for git no change scenario with a Generic error with proper no-op, early return, error configurations. This ensures that the no-op only results in log and K8s native events at normal level. Fixes a reconciliation issue when recovering from a failure state (with previous success state and artifact in the storage) and optimized git clone feature is on, which results in failure to persist as the git optimization prevented full reconciliation due to already existing artifact and removal of failure negative conditions on the object status. In order to allow failure recovery, the git clone optimizations are now only applied when the object is already in a ready state. Signed-off-by: Sunny <[email protected]>
1 parent de9dd8d commit 76245fa

File tree

1 file changed

+88
-79
lines changed

1 file changed

+88
-79
lines changed

controllers/gitrepository_controller.go

Lines changed: 88 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func (r *GitRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Reques
183183
summarize.WithReconcileError(retErr),
184184
summarize.WithIgnoreNotFound(),
185185
summarize.WithProcessors(
186-
summarize.RecordContextualError,
186+
summarize.ErrorActionHandler,
187187
summarize.RecordReconcileReq,
188188
),
189189
summarize.WithResultBuilder(sreconcile.AlwaysRequeueResultBuilder{RequeueAfter: obj.GetRequeueAfter()}),
@@ -235,10 +235,10 @@ func (r *GitRepositoryReconciler) reconcile(ctx context.Context, obj *sourcev1.G
235235
// Create temp dir for Git clone
236236
tmpDir, err := util.TempDirForObj("", obj)
237237
if err != nil {
238-
e := &serror.Event{
239-
Err: fmt.Errorf("failed to create temporary working directory: %w", err),
240-
Reason: sourcev1.DirCreationFailedReason,
241-
}
238+
e := serror.NewGeneric(
239+
fmt.Errorf("failed to create temporary working directory: %w", err),
240+
sourcev1.DirCreationFailedReason,
241+
)
242242
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
243243
return sreconcile.ResultEmpty, e
244244
}
@@ -380,10 +380,10 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
380380
}
381381
var secret corev1.Secret
382382
if err := r.Client.Get(ctx, name, &secret); err != nil {
383-
e := &serror.Event{
384-
Err: fmt.Errorf("failed to get secret '%s': %w", name.String(), err),
385-
Reason: sourcev1.AuthenticationFailedReason,
386-
}
383+
e := serror.NewGeneric(
384+
fmt.Errorf("failed to get secret '%s': %w", name.String(), err),
385+
sourcev1.AuthenticationFailedReason,
386+
)
387387
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
388388
// Return error as the world as observed may change
389389
return sreconcile.ResultEmpty, e
@@ -396,10 +396,10 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
396396
authOpts, err = git.AuthOptionsWithoutSecret(obj.Spec.URL)
397397
}
398398
if err != nil {
399-
e := &serror.Event{
400-
Err: fmt.Errorf("failed to configure auth strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
401-
Reason: sourcev1.AuthenticationFailedReason,
402-
}
399+
e := serror.NewGeneric(
400+
fmt.Errorf("failed to configure auth strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
401+
sourcev1.AuthenticationFailedReason,
402+
)
403403
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
404404
// Return error as the contents of the secret may change
405405
return sreconcile.ResultEmpty, e
@@ -415,8 +415,12 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
415415
}
416416

417417
if oc, _ := features.Enabled(features.OptimizedGitClones); oc {
418-
if artifact := obj.GetArtifact(); artifact != nil {
419-
checkoutOpts.LastRevision = artifact.Revision
418+
// Only if the object is ready, use the last revision to attempt
419+
// short-circuiting clone operation.
420+
if conditions.IsTrue(obj, meta.ReadyCondition) {
421+
if artifact := obj.GetArtifact(); artifact != nil {
422+
checkoutOpts.LastRevision = artifact.Revision
423+
}
420424
}
421425
}
422426

@@ -466,14 +470,19 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
466470
if err != nil {
467471
var v git.NoChangesError
468472
if errors.As(err, &v) {
469-
return sreconcile.ResultSuccess,
470-
&serror.Waiting{Err: v, Reason: v.Message, RequeueAfter: obj.GetRequeueAfter()}
471-
}
472-
473-
e := &serror.Event{
474-
Err: fmt.Errorf("failed to checkout and determine revision: %w", err),
475-
Reason: sourcev1.GitOperationFailedReason,
476-
}
473+
// Create generic error without notification. Since it's a no-op
474+
// error, ignore (no runtime error), normal event.
475+
ge := serror.NewGeneric(v, sourcev1.GitOperationSucceedReason)
476+
ge.Notification = false
477+
ge.Ignore = true
478+
ge.Event = corev1.EventTypeNormal
479+
return sreconcile.ResultEmpty, ge
480+
}
481+
482+
e := serror.NewGeneric(
483+
fmt.Errorf("failed to checkout and determine revision: %w", err),
484+
sourcev1.GitOperationFailedReason,
485+
)
477486
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
478487
// Coin flip on transient or persistent error, return error and hope for the best
479488
return sreconcile.ResultEmpty, e
@@ -531,58 +540,58 @@ func (r *GitRepositoryReconciler) reconcileArtifact(ctx context.Context,
531540

532541
// Ensure target path exists and is a directory
533542
if f, err := os.Stat(dir); err != nil {
534-
e := &serror.Event{
535-
Err: fmt.Errorf("failed to stat target artifact path: %w", err),
536-
Reason: sourcev1.StatOperationFailedReason,
537-
}
543+
e := serror.NewGeneric(
544+
fmt.Errorf("failed to stat target artifact path: %w", err),
545+
sourcev1.StatOperationFailedReason,
546+
)
538547
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
539548
return sreconcile.ResultEmpty, e
540549
} else if !f.IsDir() {
541-
e := &serror.Event{
542-
Err: fmt.Errorf("invalid target path: '%s' is not a directory", dir),
543-
Reason: sourcev1.InvalidPathReason,
544-
}
550+
e := serror.NewGeneric(
551+
fmt.Errorf("invalid target path: '%s' is not a directory", dir),
552+
sourcev1.InvalidPathReason,
553+
)
545554
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
546555
return sreconcile.ResultEmpty, e
547556
}
548557

549558
// Ensure artifact directory exists and acquire lock
550559
if err := r.Storage.MkdirAll(artifact); err != nil {
551-
e := &serror.Event{
552-
Err: fmt.Errorf("failed to create artifact directory: %w", err),
553-
Reason: sourcev1.DirCreationFailedReason,
554-
}
560+
e := serror.NewGeneric(
561+
fmt.Errorf("failed to create artifact directory: %w", err),
562+
sourcev1.DirCreationFailedReason,
563+
)
555564
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
556565
return sreconcile.ResultEmpty, e
557566
}
558567
unlock, err := r.Storage.Lock(artifact)
559568
if err != nil {
560-
return sreconcile.ResultEmpty, &serror.Event{
561-
Err: fmt.Errorf("failed to acquire lock for artifact: %w", err),
562-
Reason: meta.FailedReason,
563-
}
569+
return sreconcile.ResultEmpty, serror.NewGeneric(
570+
fmt.Errorf("failed to acquire lock for artifact: %w", err),
571+
meta.FailedReason,
572+
)
564573
}
565574
defer unlock()
566575

567576
// Load ignore rules for archiving
568577
ignoreDomain := strings.Split(dir, string(filepath.Separator))
569578
ps, err := sourceignore.LoadIgnorePatterns(dir, ignoreDomain)
570579
if err != nil {
571-
return sreconcile.ResultEmpty, &serror.Event{
572-
Err: fmt.Errorf("failed to load source ignore patterns from repository: %w", err),
573-
Reason: "SourceIgnoreError",
574-
}
580+
return sreconcile.ResultEmpty, serror.NewGeneric(
581+
fmt.Errorf("failed to load source ignore patterns from repository: %w", err),
582+
"SourceIgnoreError",
583+
)
575584
}
576585
if obj.Spec.Ignore != nil {
577586
ps = append(ps, sourceignore.ReadPatterns(strings.NewReader(*obj.Spec.Ignore), ignoreDomain)...)
578587
}
579588

580589
// Archive directory to storage
581590
if err := r.Storage.Archive(&artifact, dir, SourceIgnoreFilter(ps, ignoreDomain)); err != nil {
582-
e := &serror.Event{
583-
Err: fmt.Errorf("unable to archive artifact to storage: %w", err),
584-
Reason: sourcev1.ArchiveOperationFailedReason,
585-
}
591+
e := serror.NewGeneric(
592+
fmt.Errorf("unable to archive artifact to storage: %w", err),
593+
sourcev1.ArchiveOperationFailedReason,
594+
)
586595
conditions.MarkTrue(obj, sourcev1.StorageOperationFailedCondition, e.Reason, e.Err.Error())
587596
return sreconcile.ResultEmpty, e
588597
}
@@ -622,41 +631,41 @@ func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context,
622631
// Do this first as it is much cheaper than copy operations
623632
toPath, err := securejoin.SecureJoin(dir, incl.GetToPath())
624633
if err != nil {
625-
e := &serror.Event{
626-
Err: fmt.Errorf("path calculation for include '%s' failed: %w", incl.GitRepositoryRef.Name, err),
627-
Reason: "IllegalPath",
628-
}
634+
e := serror.NewGeneric(
635+
fmt.Errorf("path calculation for include '%s' failed: %w", incl.GitRepositoryRef.Name, err),
636+
"IllegalPath",
637+
)
629638
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, e.Reason, e.Err.Error())
630639
return sreconcile.ResultEmpty, e
631640
}
632641

633642
// Retrieve the included GitRepository
634643
dep := &sourcev1.GitRepository{}
635644
if err := r.Get(ctx, types.NamespacedName{Namespace: obj.Namespace, Name: incl.GitRepositoryRef.Name}, dep); err != nil {
636-
e := &serror.Event{
637-
Err: fmt.Errorf("could not get resource for include '%s': %w", incl.GitRepositoryRef.Name, err),
638-
Reason: "NotFound",
639-
}
645+
e := serror.NewGeneric(
646+
fmt.Errorf("could not get resource for include '%s': %w", incl.GitRepositoryRef.Name, err),
647+
"NotFound",
648+
)
640649
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, e.Reason, e.Err.Error())
641650
return sreconcile.ResultEmpty, e
642651
}
643652

644653
// Confirm include has an artifact
645654
if dep.GetArtifact() == nil {
646-
e := &serror.Event{
647-
Err: fmt.Errorf("no artifact available for include '%s'", incl.GitRepositoryRef.Name),
648-
Reason: "NoArtifact",
649-
}
655+
e := serror.NewGeneric(
656+
fmt.Errorf("no artifact available for include '%s'", incl.GitRepositoryRef.Name),
657+
"NoArtifact",
658+
)
650659
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, e.Reason, e.Err.Error())
651660
return sreconcile.ResultEmpty, e
652661
}
653662

654663
// Copy artifact (sub)contents to configured directory
655664
if err := r.Storage.CopyToPath(dep.GetArtifact(), incl.GetFromPath(), toPath); err != nil {
656-
e := &serror.Event{
657-
Err: fmt.Errorf("failed to copy '%s' include from %s to %s: %w", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err),
658-
Reason: "CopyFailure",
659-
}
665+
e := serror.NewGeneric(
666+
fmt.Errorf("failed to copy '%s' include from %s to %s: %w", incl.GitRepositoryRef.Name, incl.GetFromPath(), incl.GetToPath(), err),
667+
"CopyFailure",
668+
)
660669
conditions.MarkTrue(obj, sourcev1.IncludeUnavailableCondition, e.Reason, e.Err.Error())
661670
return sreconcile.ResultEmpty, e
662671
}
@@ -700,10 +709,10 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj
700709
}
701710
secret := &corev1.Secret{}
702711
if err := r.Client.Get(ctx, publicKeySecret, secret); err != nil {
703-
e := &serror.Event{
704-
Err: fmt.Errorf("PGP public keys secret error: %w", err),
705-
Reason: "VerificationError",
706-
}
712+
e := serror.NewGeneric(
713+
fmt.Errorf("PGP public keys secret error: %w", err),
714+
"VerificationError",
715+
)
707716
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
708717
return sreconcile.ResultEmpty, e
709718
}
@@ -714,10 +723,10 @@ func (r *GitRepositoryReconciler) verifyCommitSignature(ctx context.Context, obj
714723
}
715724
// Verify commit with GPG data from secret
716725
if _, err := commit.Verify(keyRings...); err != nil {
717-
e := &serror.Event{
718-
Err: fmt.Errorf("signature verification of commit '%s' failed: %w", commit.Hash.String(), err),
719-
Reason: "InvalidCommitSignature",
720-
}
726+
e := serror.NewGeneric(
727+
fmt.Errorf("signature verification of commit '%s' failed: %w", commit.Hash.String(), err),
728+
"InvalidCommitSignature",
729+
)
721730
conditions.MarkFalse(obj, sourcev1.SourceVerifiedCondition, e.Reason, e.Err.Error())
722731
// Return error in the hope the secret changes
723732
return sreconcile.ResultEmpty, e
@@ -755,10 +764,10 @@ func (r *GitRepositoryReconciler) reconcileDelete(ctx context.Context, obj *sour
755764
func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourcev1.GitRepository) error {
756765
if !obj.DeletionTimestamp.IsZero() {
757766
if deleted, err := r.Storage.RemoveAll(r.Storage.NewArtifactFor(obj.Kind, obj.GetObjectMeta(), "", "*")); err != nil {
758-
return &serror.Event{
759-
Err: fmt.Errorf("garbage collection for deleted resource failed: %w", err),
760-
Reason: "GarbageCollectionFailed",
761-
}
767+
return serror.NewGeneric(
768+
fmt.Errorf("garbage collection for deleted resource failed: %w", err),
769+
"GarbageCollectionFailed",
770+
)
762771
} else if deleted != "" {
763772
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",
764773
"garbage collected artifacts for deleted resource")
@@ -769,10 +778,10 @@ func (r *GitRepositoryReconciler) garbageCollect(ctx context.Context, obj *sourc
769778
if obj.GetArtifact() != nil {
770779
delFiles, err := r.Storage.GarbageCollect(ctx, *obj.GetArtifact(), time.Second*5)
771780
if err != nil {
772-
return &serror.Event{
773-
Err: fmt.Errorf("garbage collection of artifacts failed: %w", err),
774-
Reason: "GarbageCollectionFailed",
775-
}
781+
return serror.NewGeneric(
782+
fmt.Errorf("garbage collection of artifacts failed: %w", err),
783+
"GarbageCollectionFailed",
784+
)
776785
}
777786
if len(delFiles) > 0 {
778787
r.eventLogf(ctx, obj, events.EventTypeTrace, "GarbageCollectionSucceeded",

0 commit comments

Comments
 (0)