@@ -44,7 +44,9 @@ import (
4444 "sigs.k8s.io/controller-runtime/pkg/source"
4545
4646 imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1alpha1"
47+ "github.com/fluxcd/pkg/apis/meta"
4748 "github.com/fluxcd/pkg/runtime/events"
49+ "github.com/fluxcd/pkg/runtime/predicates"
4850 sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
4951 "github.com/fluxcd/source-controller/pkg/git"
5052
@@ -78,22 +80,36 @@ type ImageUpdateAutomationReconciler struct {
7880func (r * ImageUpdateAutomationReconciler ) Reconcile (req ctrl.Request ) (ctrl.Result , error ) {
7981 ctx := context .Background ()
8082 log := r .Log .WithValues ("imageupdateautomation" , req .NamespacedName )
83+ now := time .Now ()
8184
8285 var auto imagev1.ImageUpdateAutomation
8386 if err := r .Get (ctx , req .NamespacedName , & auto ); err != nil {
8487 return ctrl.Result {}, client .IgnoreNotFound (err )
8588 }
8689
90+ // failWithError is a helper for bailing on the reconciliation.
91+ failWithError := func (err error ) (ctrl.Result , error ) {
92+ r .event (auto , events .EventSeverityError , err .Error ())
93+ imagev1 .SetImageUpdateAutomationReadiness (& auto , metav1 .ConditionFalse , meta .ReconciliationFailedReason , err .Error ())
94+ if err := r .Status ().Update (ctx , & auto ); err != nil {
95+ log .Error (err , "failed to reconcile" )
96+ }
97+ return ctrl.Result {Requeue : true }, err
98+ }
99+
87100 // get the git repository object so it can be checked out
88101 var origin sourcev1.GitRepository
89102 originName := types.NamespacedName {
90103 Name : auto .Spec .Checkout .GitRepositoryRef .Name ,
91104 Namespace : auto .GetNamespace (),
92105 }
93106 if err := r .Get (ctx , originName , & origin ); err != nil {
94- // TODO status
95107 if client .IgnoreNotFound (err ) == nil {
108+ imagev1 .SetImageUpdateAutomationReadiness (& auto , metav1 .ConditionFalse , imagev1 .GitNotAvailableReason , "referenced git repository is missing" )
96109 log .Error (err , "referenced git repository does not exist" )
110+ if err := r .Status ().Update (ctx , & auto ); err != nil {
111+ return ctrl.Result {Requeue : true }, err
112+ }
97113 return ctrl.Result {}, nil // and assume we'll hear about it when it arrives
98114 }
99115 return ctrl.Result {}, err
@@ -103,22 +119,20 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(req ctrl.Request) (ctrl.Resu
103119
104120 tmp , err := ioutil .TempDir ("" , fmt .Sprintf ("%s-%s" , originName .Namespace , originName .Name ))
105121 if err != nil {
106- // TODO status
107- return ctrl.Result {}, err
122+ return failWithError (err )
108123 }
109124 defer os .RemoveAll (tmp )
110125
111126 // FIXME use context with deadline for at least the following ops
112127
113128 access , err := r .getRepoAccess (ctx , & origin )
114129 if err != nil {
115- return ctrl. Result {}, err
130+ return failWithError ( err )
116131 }
117132
118133 var repo * gogit.Repository
119134 if repo , err = cloneInto (ctx , access , auto .Spec .Checkout .Branch , tmp ); err != nil {
120- // TODO status
121- return ctrl.Result {}, err
135+ return failWithError (err )
122136 }
123137
124138 log .V (debug ).Info ("cloned git repository" , "gitrepository" , originName , "branch" , auto .Spec .Checkout .Branch , "working" , tmp )
@@ -131,59 +145,53 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(req ctrl.Request) (ctrl.Resu
131145 // could be filtered by the automation object).
132146 var policies imagev1_reflect.ImagePolicyList
133147 if err := r .List (ctx , & policies , & client.ListOptions {Namespace : req .NamespacedName .Namespace }); err != nil {
134- return ctrl. Result {}, err
148+ return failWithError ( err )
135149 }
136150
137151 if err := updateAccordingToSetters (ctx , tmp , policies .Items ); err != nil {
138- r .event (auto , events .EventSeverityError , err .Error ())
139- return ctrl.Result {}, err
152+ return failWithError (err )
140153 }
141154 default :
142155 log .Info ("no update strategy given in the spec" )
143156 // no sense rescheduling until this resource changes
144- return ctrl.Result {}, nil
157+ r .event (auto , events .EventSeverityInfo , "no update strategy in spec, failing trivially" )
158+ imagev1 .SetImageUpdateAutomationReadiness (& auto , metav1 .ConditionFalse , imagev1 .NoStrategyReason , "no update strategy is given for object" )
159+ err := r .Status ().Update (ctx , & auto )
160+ return ctrl.Result {}, err
145161 }
146162
147163 log .V (debug ).Info ("ran updates to working dir" , "working" , tmp )
148164
149- var commitMade bool
165+ var statusMessage string
150166
151167 if rev , err := commitAllAndPush (ctx , repo , access , & auto .Spec .Commit ); err != nil {
152168 if err == errNoChanges {
153- log .Info ("no changes made in working directory; no commit" )
169+ r .event (auto , events .EventSeverityInfo , "no updates made" )
170+ log .V (debug ).Info ("no changes made in working directory; no commit" )
171+ statusMessage = "no updates made"
154172 } else {
155- r .event (auto , events .EventSeverityError , err .Error ())
156- return ctrl.Result {}, err
173+ return failWithError (err )
157174 }
158175 } else {
159- commitMade = true
160176 r .event (auto , events .EventSeverityInfo , "committed and pushed change " + rev )
161- log .V (debug ).Info ("pushed commit to origin" , "revision" , rev )
177+ log .Info ("pushed commit to origin" , "revision" , rev )
178+ statusMessage = "committed and pushed " + rev
162179 }
163180
164- // The status is not updated unless a commit was made, OR it's
165- // been at least interval since the last run (in which case,
166- // assume this is a periodic run). This is so there's a fixed
167- // point -- otherwise, the fact of the status change would mean it
168- // gets queued again.
169-
170- now := time .Now ()
171- interval := intervalOrDefault (& auto )
172- sinceLast := durationSinceLastRun (& auto , now )
173-
174- when := interval
175-
176- if commitMade || sinceLast >= interval {
177- auto .Status .LastAutomationRunTime = & metav1.Time {Time : now }
178- if err = r .Status ().Update (ctx , & auto ); err != nil {
179- return ctrl.Result {}, err
180- }
181- } else {
182- // requeue for the remainder of the interval
183- when = interval - sinceLast
181+ // Getting to here is a successful run.
182+ auto .Status .LastAutomationRunTime = & metav1.Time {Time : now }
183+ imagev1 .SetImageUpdateAutomationReadiness (& auto , metav1 .ConditionTrue , meta .ReconciliationSucceededReason , statusMessage )
184+ if err = r .Status ().Update (ctx , & auto ); err != nil {
185+ return ctrl.Result {Requeue : true }, err
184186 }
185187
186- return ctrl.Result {RequeueAfter : when }, nil
188+ // We're either in this method because something changed, or this
189+ // object got requeued. Either way, once successful, we don't need
190+ // to see the object again until Interval has passed, or something
191+ // changes again.
192+
193+ interval := intervalOrDefault (& auto )
194+ return ctrl.Result {RequeueAfter : interval }, nil
187195}
188196
189197func (r * ImageUpdateAutomationReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
@@ -199,6 +207,7 @@ func (r *ImageUpdateAutomationReconciler) SetupWithManager(mgr ctrl.Manager) err
199207
200208 return ctrl .NewControllerManagedBy (mgr ).
201209 For (& imagev1.ImageUpdateAutomation {}).
210+ WithEventFilter (predicates.ChangePredicate {}).
202211 Watches (& source.Kind {Type : & sourcev1.GitRepository {}},
203212 & handler.EnqueueRequestsFromMapFunc {
204213 ToRequests : handler .ToRequestsFunc (r .automationsForGitRepo ),
@@ -214,9 +223,9 @@ func intervalOrDefault(auto *imagev1.ImageUpdateAutomation) time.Duration {
214223 return defaultInterval
215224}
216225
217- // durationUntilNextRun gives the length of time to wait before
218- // running the automation again after a successful run, unless
219- // something (a dependency) changes to trigger a run.
226+ // durationSinceLastRun calculates how long it's been since the last
227+ // time the automation ran (which you can then use to find how long to
228+ // wait until the next run) .
220229func durationSinceLastRun (auto * imagev1.ImageUpdateAutomation , now time.Time ) time.Duration {
221230 last := auto .Status .LastAutomationRunTime
222231 if last == nil {
0 commit comments