@@ -18,6 +18,7 @@ import (
18
18
"context"
19
19
"encoding/json"
20
20
"errors"
21
+ "fmt"
21
22
"math/rand"
22
23
"os"
23
24
"strconv"
@@ -81,6 +82,9 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
81
82
if ds , ok := u .GetAnnotations ()[ReconcilePeriodAnnotation ]; ok {
82
83
duration , err := time .ParseDuration (ds )
83
84
if err != nil {
85
+ // Should attempt to update to a failed condition
86
+ r .markError (u , request .NamespacedName , fmt .Sprintf ("Unable to parse reconcile period annotation: %v" , err ))
87
+ logger .Error (err , "Unable to parse reconcile period annotation" )
84
88
return reconcileResult , err
85
89
}
86
90
reconcileResult .RequeueAfter = duration
@@ -96,6 +100,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
96
100
u .SetFinalizers (finalizers )
97
101
err := r .Client .Update (context .TODO (), u )
98
102
if err != nil {
103
+ logger .Error (err , "Unable to update cr with finalizer" )
99
104
return reconcileResult , err
100
105
}
101
106
}
@@ -106,18 +111,19 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
106
111
107
112
spec := u .Object ["spec" ]
108
113
_ , ok := spec .(map [string ]interface {})
114
+ // Need to handle cases where there is no spec.
115
+ // We can add the spec to the object, which will allow
116
+ // everything to work, and will not get updated.
117
+ // Therefore we can now deal with the case of secrets and configmaps.
109
118
if ! ok {
110
119
logger .V (1 ).Info ("Spec was not found" )
111
120
u .Object ["spec" ] = map [string ]interface {}{}
112
- err = r .Client .Update (context .TODO (), u )
113
- if err != nil {
114
- return reconcileResult , err
115
- }
116
121
}
117
122
118
123
if r .ManageStatus {
119
124
err = r .markRunning (u , request .NamespacedName )
120
125
if err != nil {
126
+ logger .Error (err , "Unable to update the status to mark cr as running" )
121
127
return reconcileResult , err
122
128
}
123
129
}
@@ -131,6 +137,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
131
137
132
138
kc , err := kubeconfig .Create (ownerRef , "http://localhost:8888" , u .GetNamespace ())
133
139
if err != nil {
140
+ r .markError (u , request .NamespacedName , "Unable to run reconciliation" )
141
+ logger .Error (err , "Unable to generate kubeconfig" )
134
142
return reconcileResult , err
135
143
}
136
144
defer func () {
@@ -140,6 +148,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
140
148
}()
141
149
result , err := r .Runner .Run (ident , u , kc .Name ())
142
150
if err != nil {
151
+ r .markError (u , request .NamespacedName , "Unable to run reconciliation" )
152
+ logger .Error (err , "Unable to run ansible runner" )
143
153
return reconcileResult , err
144
154
}
145
155
@@ -161,7 +171,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
161
171
return reconcile.Result {}, err
162
172
}
163
173
}
164
- if event .Event == eventapi .EventRunnerOnFailed {
174
+ if event .Event == eventapi .EventRunnerOnFailed && ! event . IgnoreError () {
165
175
failureMessages = append (failureMessages , event .GetFailedPlaybookMessage ())
166
176
}
167
177
}
@@ -189,6 +199,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
189
199
u .SetFinalizers (finalizers )
190
200
err := r .Client .Update (context .TODO (), u )
191
201
if err != nil {
202
+ logger .Error (err , "Failed to remove finalizer" )
192
203
return reconcileResult , err
193
204
}
194
205
}
@@ -237,6 +248,47 @@ func (r *AnsibleOperatorReconciler) markRunning(u *unstructured.Unstructured, na
237
248
return nil
238
249
}
239
250
251
+ // markError - used to alert the user to the issues during the validation of a reconcile run.
252
+ // i.e Annotations that could be incorrect
253
+ func (r * AnsibleOperatorReconciler ) markError (u * unstructured.Unstructured , namespacedName types.NamespacedName , failureMessage string ) error {
254
+ logger := logf .Log .WithName ("markError" )
255
+ // Get the latest resource to prevent updating a stale status
256
+ err := r .Client .Get (context .TODO (), namespacedName , u )
257
+ if apierrors .IsNotFound (err ) {
258
+ logger .Info ("Resource not found, assuming it was deleted" , err )
259
+ return nil
260
+ }
261
+ if err != nil {
262
+ return err
263
+ }
264
+ statusInterface := u .Object ["status" ]
265
+ statusMap , ok := statusInterface .(map [string ]interface {})
266
+ // If the map is not available create one.
267
+ if ! ok {
268
+ statusMap = map [string ]interface {}{}
269
+ }
270
+ crStatus := ansiblestatus .CreateFromMap (statusMap )
271
+
272
+ sc := ansiblestatus .GetCondition (crStatus , ansiblestatus .RunningConditionType )
273
+ if sc != nil {
274
+ sc .Status = v1 .ConditionFalse
275
+ ansiblestatus .SetCondition (& crStatus , * sc )
276
+ }
277
+
278
+ c := ansiblestatus .NewCondition (
279
+ ansiblestatus .FailureConditionType ,
280
+ v1 .ConditionTrue ,
281
+ nil ,
282
+ ansiblestatus .FailedReason ,
283
+ failureMessage ,
284
+ )
285
+ ansiblestatus .SetCondition (& crStatus , * c )
286
+ // This needs the status subresource to be enabled by default.
287
+ u .Object ["status" ] = crStatus .GetJSONMap ()
288
+
289
+ return r .Client .Status ().Update (context .TODO (), u )
290
+ }
291
+
240
292
func (r * AnsibleOperatorReconciler ) markDone (u * unstructured.Unstructured , namespacedName types.NamespacedName , statusEvent eventapi.StatusJobEvent , failureMessages eventapi.FailureMessages ) error {
241
293
logger := logf .Log .WithName ("markDone" )
242
294
// Get the latest resource to prevent updating a stale status
0 commit comments