@@ -55,6 +55,7 @@ type AnsibleOperatorReconciler struct {
55
55
GVK schema.GroupVersionKind
56
56
Runner runner.Runner
57
57
Client client.Client
58
+ APIReader client.Reader
58
59
EventHandlers []events.EventHandler
59
60
ReconcilePeriod time.Duration
60
61
ManageStatus bool
@@ -84,7 +85,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
84
85
duration , err := time .ParseDuration (ds )
85
86
if err != nil {
86
87
// Should attempt to update to a failed condition
87
- if errmark := r .markError (u , request .NamespacedName , fmt .Sprintf ("Unable to parse reconcile period annotation: %v" , err )); errmark != nil {
88
+ errmark := r .markError (u , request .NamespacedName , fmt .Sprintf ("Unable to parse reconcile period annotation: %v" , err ))
89
+ if errmark != nil {
88
90
logger .Error (errmark , "Unable to mark error annotation" )
89
91
}
90
92
logger .Error (err , "Unable to parse reconcile period annotation" )
@@ -124,10 +126,10 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
124
126
}
125
127
126
128
if r .ManageStatus {
127
- err = r .markRunning (u , request .NamespacedName )
128
- if err != nil {
129
- logger .Error (err , "Unable to update the status to mark cr as running" )
130
- return reconcileResult , err
129
+ errmark : = r .markRunning (u , request .NamespacedName )
130
+ if errmark != nil {
131
+ logger .Error (errmark , "Unable to update the status to mark cr as running" )
132
+ return reconcileResult , errmark
131
133
}
132
134
}
133
135
@@ -140,7 +142,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
140
142
141
143
kc , err := kubeconfig .Create (ownerRef , "http://localhost:8888" , u .GetNamespace ())
142
144
if err != nil {
143
- if errmark := r .markError (u , request .NamespacedName , "Unable to run reconciliation" ); errmark != nil {
145
+ errmark := r .markError (u , request .NamespacedName , "Unable to run reconciliation" )
146
+ if errmark != nil {
144
147
logger .Error (errmark , "Unable to mark error to run reconciliation" )
145
148
}
146
149
logger .Error (err , "Unable to generate kubeconfig" )
@@ -153,7 +156,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
153
156
}()
154
157
result , err := r .Runner .Run (ident , u , kc .Name ())
155
158
if err != nil {
156
- if errmark := r .markError (u , request .NamespacedName , "Unable to run reconciliation" ); errmark != nil {
159
+ errmark := r .markError (u , request .NamespacedName , "Unable to run reconciliation" )
160
+ if errmark != nil {
157
161
logger .Error (errmark , "Unable to mark error to run reconciliation" )
158
162
}
159
163
logger .Error (err , "Unable to run ansible runner" )
@@ -193,13 +197,13 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
193
197
return reconcileResult , eventErr
194
198
}
195
199
196
- // Need to get the unstructured object after ansible
197
- // this needs to hit the API
198
- err = r .Client .Get (context .TODO (), request .NamespacedName , u )
199
- if apierrors .IsNotFound (err ) {
200
- return reconcile.Result {}, nil
201
- }
200
+ // Need to get the unstructured object after the Ansible runner finishes.
201
+ // This needs to hit the API server to retrieve updates.
202
+ err = r .APIReader .Get (context .TODO (), request .NamespacedName , u )
202
203
if err != nil {
204
+ if apierrors .IsNotFound (err ) {
205
+ return reconcile.Result {}, nil
206
+ }
203
207
return reconcile.Result {}, err
204
208
}
205
209
@@ -225,27 +229,24 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
225
229
}
226
230
}
227
231
if r .ManageStatus {
228
- err = r .markDone (u , request .NamespacedName , statusEvent , failureMessages )
229
- if err != nil {
230
- logger .Error (err , "Failed to mark status done" )
232
+ errmark : = r .markDone (u , request .NamespacedName , statusEvent , failureMessages )
233
+ if errmark != nil {
234
+ logger .Error (errmark , "Failed to mark status done" )
231
235
}
236
+ return reconcileResult , errmark
232
237
}
233
- return reconcileResult , err
238
+ return reconcileResult , nil
234
239
}
235
240
236
241
func (r * AnsibleOperatorReconciler ) markRunning (u * unstructured.Unstructured , namespacedName types.NamespacedName ) error {
237
- // Get the latest resource to prevent updating a stale status
238
- err := r .Client .Get (context .TODO (), namespacedName , u )
239
- if err != nil {
242
+ // Get the latest resource to prevent updating a stale status.
243
+ if err := r .APIReader .Get (context .TODO (), namespacedName , u ); err != nil {
240
244
return err
241
245
}
242
- statusInterface := u .Object ["status" ]
243
- statusMap , _ := statusInterface .(map [string ]interface {})
244
- crStatus := ansiblestatus .CreateFromMap (statusMap )
246
+ crStatus := getStatus (u )
245
247
246
248
// If there is no current status add that we are working on this resource.
247
249
errCond := ansiblestatus .GetCondition (crStatus , ansiblestatus .FailureConditionType )
248
-
249
250
if errCond != nil {
250
251
errCond .Status = v1 .ConditionFalse
251
252
ansiblestatus .SetCondition (& crStatus , * errCond )
@@ -262,34 +263,26 @@ func (r *AnsibleOperatorReconciler) markRunning(u *unstructured.Unstructured, na
262
263
)
263
264
ansiblestatus .SetCondition (& crStatus , * c )
264
265
u .Object ["status" ] = crStatus .GetJSONMap ()
265
- err = r .Client .Status ().Update (context .TODO (), u )
266
- if err != nil {
267
- return err
268
- }
269
- return nil
266
+
267
+ return r .Client .Status ().Update (context .TODO (), u )
270
268
}
271
269
272
270
// markError - used to alert the user to the issues during the validation of a reconcile run.
273
271
// i.e Annotations that could be incorrect
274
272
func (r * AnsibleOperatorReconciler ) markError (u * unstructured.Unstructured , namespacedName types.NamespacedName , failureMessage string ) error {
275
273
logger := logf .Log .WithName ("markError" )
274
+ // Immediately update metrics with failed reconciliation, since Get()
275
+ // may fail.
276
276
metrics .ReconcileFailed (r .GVK .String ())
277
- // Get the latest resource to prevent updating a stale status
278
- err := r .Client .Get (context .TODO (), namespacedName , u )
279
- if apierrors .IsNotFound (err ) {
280
- logger .Info ("Resource not found, assuming it was deleted" )
281
- return nil
282
- }
283
- if err != nil {
277
+ // Get the latest resource to prevent updating a stale status.
278
+ if err := r .APIReader .Get (context .TODO (), namespacedName , u ); err != nil {
279
+ if apierrors .IsNotFound (err ) {
280
+ logger .Info ("Resource not found, assuming it was deleted" )
281
+ return nil
282
+ }
284
283
return err
285
284
}
286
- statusInterface := u .Object ["status" ]
287
- statusMap , ok := statusInterface .(map [string ]interface {})
288
- // If the map is not available create one.
289
- if ! ok {
290
- statusMap = map [string ]interface {}{}
291
- }
292
- crStatus := ansiblestatus .CreateFromMap (statusMap )
285
+ crStatus := getStatus (u )
293
286
294
287
sc := ansiblestatus .GetCondition (crStatus , ansiblestatus .RunningConditionType )
295
288
if sc != nil {
@@ -313,27 +306,26 @@ func (r *AnsibleOperatorReconciler) markError(u *unstructured.Unstructured, name
313
306
314
307
func (r * AnsibleOperatorReconciler ) markDone (u * unstructured.Unstructured , namespacedName types.NamespacedName , statusEvent eventapi.StatusJobEvent , failureMessages eventapi.FailureMessages ) error {
315
308
logger := logf .Log .WithName ("markDone" )
316
- // Get the latest resource to prevent updating a stale status
317
- err := r .Client .Get (context .TODO (), namespacedName , u )
318
- if apierrors .IsNotFound (err ) {
319
- logger .Info ("Resource not found, assuming it was deleted" )
320
- return nil
321
- }
322
- if err != nil {
309
+ // Get the latest resource to prevent updating a stale status.
310
+ if err := r .APIReader .Get (context .TODO (), namespacedName , u ); err != nil {
311
+ if apierrors .IsNotFound (err ) {
312
+ logger .Info ("Resource not found, assuming it was deleted" )
313
+ return nil
314
+ }
323
315
return err
324
316
}
325
- statusInterface := u .Object ["status" ]
326
- statusMap , _ := statusInterface .(map [string ]interface {})
327
- crStatus := ansiblestatus .CreateFromMap (statusMap )
317
+ crStatus := getStatus (u )
328
318
329
319
runSuccessful := len (failureMessages ) == 0
330
320
ansibleStatus := ansiblestatus .NewAnsibleResultFromStatusJobEvent (statusEvent )
331
321
332
322
if ! runSuccessful {
333
323
metrics .ReconcileFailed (r .GVK .String ())
334
324
sc := ansiblestatus .GetCondition (crStatus , ansiblestatus .RunningConditionType )
335
- sc .Status = v1 .ConditionFalse
336
- ansiblestatus .SetCondition (& crStatus , * sc )
325
+ if sc != nil {
326
+ sc .Status = v1 .ConditionFalse
327
+ ansiblestatus .SetCondition (& crStatus , * sc )
328
+ }
337
329
c := ansiblestatus .NewCondition (
338
330
ansiblestatus .FailureConditionType ,
339
331
v1 .ConditionTrue ,
@@ -369,3 +361,14 @@ func contains(l []string, s string) bool {
369
361
}
370
362
return false
371
363
}
364
+
365
+ // getStatus returns u's "status" block as a status.Status.
366
+ func getStatus (u * unstructured.Unstructured ) ansiblestatus.Status {
367
+ statusInterface := u .Object ["status" ]
368
+ statusMap , ok := statusInterface .(map [string ]interface {})
369
+ // If the map is not available create one.
370
+ if ! ok {
371
+ statusMap = map [string ]interface {}{}
372
+ }
373
+ return ansiblestatus .CreateFromMap (statusMap )
374
+ }
0 commit comments