Skip to content

Commit 4ab4996

Browse files
author
Shawn Hurley
authored
pkg/ansible: Updating reconciler to make errors more obvious (#1192)
* Taking a pass through the error scenarios and adding context to them * Adding more obvious errors for end users around the reconcile period errors
1 parent 1d54532 commit 4ab4996

File tree

2 files changed

+69
-5
lines changed

2 files changed

+69
-5
lines changed

pkg/ansible/controller/reconcile.go

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/json"
2020
"errors"
21+
"fmt"
2122
"math/rand"
2223
"os"
2324
"strconv"
@@ -81,6 +82,9 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
8182
if ds, ok := u.GetAnnotations()[ReconcilePeriodAnnotation]; ok {
8283
duration, err := time.ParseDuration(ds)
8384
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")
8488
return reconcileResult, err
8589
}
8690
reconcileResult.RequeueAfter = duration
@@ -96,6 +100,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
96100
u.SetFinalizers(finalizers)
97101
err := r.Client.Update(context.TODO(), u)
98102
if err != nil {
103+
logger.Error(err, "Unable to update cr with finalizer")
99104
return reconcileResult, err
100105
}
101106
}
@@ -106,18 +111,19 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
106111

107112
spec := u.Object["spec"]
108113
_, 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.
109118
if !ok {
110119
logger.V(1).Info("Spec was not found")
111120
u.Object["spec"] = map[string]interface{}{}
112-
err = r.Client.Update(context.TODO(), u)
113-
if err != nil {
114-
return reconcileResult, err
115-
}
116121
}
117122

118123
if r.ManageStatus {
119124
err = r.markRunning(u, request.NamespacedName)
120125
if err != nil {
126+
logger.Error(err, "Unable to update the status to mark cr as running")
121127
return reconcileResult, err
122128
}
123129
}
@@ -131,6 +137,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
131137

132138
kc, err := kubeconfig.Create(ownerRef, "http://localhost:8888", u.GetNamespace())
133139
if err != nil {
140+
r.markError(u, request.NamespacedName, "Unable to run reconciliation")
141+
logger.Error(err, "Unable to generate kubeconfig")
134142
return reconcileResult, err
135143
}
136144
defer func() {
@@ -140,6 +148,8 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
140148
}()
141149
result, err := r.Runner.Run(ident, u, kc.Name())
142150
if err != nil {
151+
r.markError(u, request.NamespacedName, "Unable to run reconciliation")
152+
logger.Error(err, "Unable to run ansible runner")
143153
return reconcileResult, err
144154
}
145155

@@ -161,7 +171,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
161171
return reconcile.Result{}, err
162172
}
163173
}
164-
if event.Event == eventapi.EventRunnerOnFailed {
174+
if event.Event == eventapi.EventRunnerOnFailed && !event.IgnoreError() {
165175
failureMessages = append(failureMessages, event.GetFailedPlaybookMessage())
166176
}
167177
}
@@ -189,6 +199,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
189199
u.SetFinalizers(finalizers)
190200
err := r.Client.Update(context.TODO(), u)
191201
if err != nil {
202+
logger.Error(err, "Failed to remove finalizer")
192203
return reconcileResult, err
193204
}
194205
}
@@ -237,6 +248,47 @@ func (r *AnsibleOperatorReconciler) markRunning(u *unstructured.Unstructured, na
237248
return nil
238249
}
239250

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+
240292
func (r *AnsibleOperatorReconciler) markDone(u *unstructured.Unstructured, namespacedName types.NamespacedName, statusEvent eventapi.StatusJobEvent, failureMessages eventapi.FailureMessages) error {
241293
logger := logf.Log.WithName("markDone")
242294
// Get the latest resource to prevent updating a stale status

pkg/ansible/runner/eventapi/types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,15 @@ func (je JobEvent) GetFailedPlaybookMessage() string {
110110
}
111111
return message
112112
}
113+
114+
// IgnoreError - Does the job event contain the ignore_error ansible flag
115+
func (je JobEvent) IgnoreError() bool {
116+
ignoreErrors, ok := je.EventData["ignore_errors"]
117+
if !ok {
118+
return false
119+
}
120+
if b, ok := ignoreErrors.(bool); ok && b {
121+
return b
122+
}
123+
return false
124+
}

0 commit comments

Comments
 (0)