@@ -7,12 +7,15 @@ package controllers
77
88import (
99 "context"
10+ "errors"
1011 "fmt"
11- "time"
1212
13+ "github.com/fluxcd/pkg/apis/meta"
14+ "github.com/fluxcd/pkg/runtime/conditions"
1315 "github.com/fluxcd/pkg/runtime/patch"
1416 corev1 "k8s.io/api/core/v1"
1517 apierrors "k8s.io/apimachinery/pkg/api/errors"
18+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1619 "k8s.io/apimachinery/pkg/runtime"
1720 "k8s.io/apimachinery/pkg/types"
1821 ctrl "sigs.k8s.io/controller-runtime"
@@ -44,71 +47,134 @@ type GitSyncReconciler struct {
4447// Reconcile is part of the main kubernetes reconciliation loop which aims to
4548// move the current state of the cluster closer to the desired state.
4649func (r * GitSyncReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
50+ var (
51+ result ctrl.Result
52+ retErr error
53+ )
54+
4755 log := log .FromContext (ctx )
4856 log .V (4 ).Info ("starting reconcile loop for snapshot" )
49- gitSync := & v1alpha1.GitSync {}
50- if err := r .Get (ctx , req .NamespacedName , gitSync ); err != nil {
57+ obj := & v1alpha1.GitSync {}
58+ if err := r .Get (ctx , req .NamespacedName , obj ); err != nil {
5159 if apierrors .IsNotFound (err ) {
5260 return ctrl.Result {}, nil
5361 }
5462 return ctrl.Result {}, fmt .Errorf ("failed to get git sync object: %w" , err )
5563 }
56- log .V (4 ).Info ("found reconciling object" , "gitSync" , gitSync )
64+ log .V (4 ).Info ("found reconciling object" , "gitSync" , obj )
5765
58- if gitSync .Status .Digest != "" {
59- log .Info ("GitSync object already synced; status contains digest information" , "digest" , gitSync .Status .Digest )
66+ if obj .Status .Digest != "" {
67+ log .Info ("GitSync object already synced; status contains digest information" , "digest" , obj .Status .Digest )
6068 return ctrl.Result {}, nil
6169 }
6270
71+ // The replication controller doesn't need a shouldReconcile, because it should always reconcile,
72+ // that is its purpose.
73+ patchHelper , err := patch .NewHelper (obj , r .Client )
74+ if err != nil {
75+ retErr = errors .Join (retErr , err )
76+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .PatchFailedReason , err .Error ())
77+
78+ return ctrl.Result {}, retErr
79+ }
80+
81+ // Always attempt to patch the object and status after each reconciliation.
82+ defer func () {
83+ // Patching has not been set up, or the controller errored earlier.
84+ if patchHelper == nil {
85+ return
86+ }
87+
88+ if condition := conditions .Get (obj , meta .StalledCondition ); condition != nil && condition .Status == metav1 .ConditionTrue {
89+ conditions .Delete (obj , meta .ReconcilingCondition )
90+ }
91+
92+ // Check if it's a successful reconciliation.
93+ // We don't set Requeue in case of error, so we can safely check for Requeue.
94+ if result .RequeueAfter == obj .GetRequeueAfter () && ! result .Requeue && retErr == nil {
95+ // Remove the reconciling condition if it's set.
96+ conditions .Delete (obj , meta .ReconcilingCondition )
97+
98+ // Set the return err as the ready failure message if the resource is not ready, but also not reconciling or stalled.
99+ if ready := conditions .Get (obj , meta .ReadyCondition ); ready != nil && ready .Status == metav1 .ConditionFalse && ! conditions .IsStalled (obj ) {
100+ retErr = errors .New (conditions .GetMessage (obj , meta .ReadyCondition ))
101+ }
102+ }
103+
104+ // If still reconciling then reconciliation did not succeed, set to ProgressingWithRetry to
105+ // indicate that reconciliation will be retried.
106+ if conditions .IsReconciling (obj ) {
107+ reconciling := conditions .Get (obj , meta .ReconcilingCondition )
108+ reconciling .Reason = meta .ProgressingWithRetryReason
109+ conditions .Set (obj , reconciling )
110+ }
111+
112+ // If not reconciling or stalled than mark Ready=True
113+ if ! conditions .IsReconciling (obj ) &&
114+ ! conditions .IsStalled (obj ) &&
115+ retErr == nil &&
116+ result .RequeueAfter == obj .GetRequeueAfter () {
117+ conditions .MarkTrue (obj , meta .ReadyCondition , meta .SucceededReason , "Reconciliation success" )
118+ }
119+
120+ // Set status observed generation option if the component is stalled or ready.
121+ if conditions .IsStalled (obj ) || conditions .IsReady (obj ) {
122+ obj .Status .ObservedGeneration = obj .Generation
123+ }
124+
125+ // Update the object.
126+ if err := patchHelper .Patch (ctx , obj ); err != nil {
127+ retErr = errors .Join (retErr , err )
128+ }
129+ }()
130+
63131 snapshot := & ocmv1.Snapshot {}
64132 if err := r .Get (ctx , types.NamespacedName {
65- Namespace : gitSync .Spec .SnapshotRef .Namespace ,
66- Name : gitSync .Spec .SnapshotRef .Name ,
133+ Namespace : obj .Spec .SnapshotRef .Namespace ,
134+ Name : obj .Spec .SnapshotRef .Name ,
67135 }, snapshot ); err != nil {
68- return ctrl.Result {}, fmt .Errorf ("failed to find snapshot: %w" , err )
136+ retErr = fmt .Errorf ("failed to find snapshot: %w" , err )
137+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .SnapshotGetFailedReason , retErr .Error ())
138+
139+ return ctrl.Result {}, retErr
69140 }
141+
70142 authSecret := & corev1.Secret {}
71143 if err := r .Get (ctx , types.NamespacedName {
72- Namespace : gitSync .Spec .AuthRef .Namespace ,
73- Name : gitSync .Spec .AuthRef .Name ,
144+ Namespace : obj .Spec .AuthRef .Namespace ,
145+ Name : obj .Spec .AuthRef .Name ,
74146 }, authSecret ); err != nil {
75- return ctrl.Result {}, fmt .Errorf ("failed to find authentication secret: %w" , err )
147+ retErr = fmt .Errorf ("failed to find authentication secret: %w" , err )
148+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .AuthenticateGetFailedReason , retErr .Error ())
149+
150+ return ctrl.Result {}, retErr
76151 }
77152
78153 // trim any trailing `/` and then just add.
79154 log .V (4 ).Info ("crafting artifact URL to download from" , "url" , snapshot .Status .RepositoryURL )
80155 opts := & providers.PushOptions {
81- URL : gitSync .Spec .URL ,
82- Message : gitSync .Spec .CommitTemplate .Message ,
83- Name : gitSync .Spec .CommitTemplate .Name ,
84- Email : gitSync .Spec .CommitTemplate .Email ,
156+ URL : obj .Spec .URL ,
157+ Message : obj .Spec .CommitTemplate .Message ,
158+ Name : obj .Spec .CommitTemplate .Name ,
159+ Email : obj .Spec .CommitTemplate .Email ,
85160 Snapshot : snapshot ,
86- Branch : gitSync .Spec .Branch ,
87- SubPath : gitSync .Spec .SubPath ,
161+ Branch : obj .Spec .Branch ,
162+ SubPath : obj .Spec .SubPath ,
88163 }
164+
89165 r .parseAuthSecret (authSecret , opts )
90166
91167 digest , err := r .Git .Push (ctx , opts )
92168 if err != nil {
93- return ctrl.Result {}, fmt .Errorf ("failed to push to git repository: %w" , err )
94- }
95- // Initialize the patch helper.
96- patchHelper , err := patch .NewHelper (gitSync , r .Client )
97- if err != nil {
98- return ctrl.Result {
99- RequeueAfter : 1 * time .Minute ,
100- }, fmt .Errorf ("failed to create patch helper: %w" , err )
101- }
169+ retErr = fmt .Errorf ("failed to push to git repository: %w" , err )
170+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .GitRepositoryPushFailedReason , retErr .Error ())
102171
103- gitSync .Status .Digest = digest
104- if err := patchHelper .Patch (ctx , gitSync ); err != nil {
105- return ctrl.Result {
106- RequeueAfter : 1 * time .Minute ,
107- }, fmt .Errorf ("failed to patch git sync object: %w" , err )
172+ return ctrl.Result {}, retErr
108173 }
109- log .V (4 ).Info ("patch successful" )
110174
111- return ctrl.Result {}, nil
175+ obj .Status .Digest = digest
176+
177+ return result , retErr
112178}
113179
114180// SetupWithManager sets up the controller with the Manager.
0 commit comments