Skip to content

Commit fc9b6b1

Browse files
authored
Merge pull request #713 from mhrivnak/ansible-error
Improves output available for troubleshooting ansible
2 parents fa20007 + cc3b330 commit fc9b6b1

File tree

5 files changed

+64
-18
lines changed

5 files changed

+64
-18
lines changed

pkg/ansible/controller/reconcile.go

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import (
1818
"context"
1919
"encoding/json"
2020
"errors"
21+
"math/rand"
2122
"os"
23+
"strconv"
2224
"strings"
2325
"time"
2426

@@ -65,6 +67,15 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
6567
if err != nil {
6668
return reconcile.Result{}, err
6769
}
70+
71+
ident := strconv.Itoa(rand.Int())
72+
logger := logrus.WithFields(logrus.Fields{
73+
"component": "reconciler",
74+
"job": ident,
75+
"name": u.GetName(),
76+
"namespace": u.GetNamespace(),
77+
})
78+
6879
reconcileResult := reconcile.Result{RequeueAfter: r.ReconcilePeriod}
6980
if ds, ok := u.GetAnnotations()[ReconcilePeriodAnnotation]; ok {
7081
duration, err := time.ParseDuration(ds)
@@ -79,7 +90,7 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
7990
pendingFinalizers := u.GetFinalizers()
8091
// If the resource is being deleted we don't want to add the finalizer again
8192
if finalizerExists && !deleted && !contains(pendingFinalizers, finalizer) {
82-
logrus.Debugf("Adding finalizer %s to resource", finalizer)
93+
logger.Debugf("Adding finalizer %s to resource", finalizer)
8394
finalizers := append(pendingFinalizers, finalizer)
8495
u.SetFinalizers(finalizers)
8596
err := r.Client.Update(context.TODO(), u)
@@ -88,14 +99,14 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
8899
}
89100
}
90101
if !contains(pendingFinalizers, finalizer) && deleted {
91-
logrus.Info("Resource is terminated, skipping reconcilation")
102+
logger.Info("Resource is terminated, skipping reconcilation")
92103
return reconcileResult, nil
93104
}
94105

95106
spec := u.Object["spec"]
96107
_, ok := spec.(map[string]interface{})
97108
if !ok {
98-
logrus.Debugf("spec was not found")
109+
logger.Debugf("spec was not found")
99110
u.Object["spec"] = map[string]interface{}{}
100111
err = r.Client.Update(context.TODO(), u)
101112
if err != nil {
@@ -141,17 +152,17 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
141152
return reconcileResult, err
142153
}
143154
defer os.Remove(kc.Name())
144-
eventChan, err := r.Runner.Run(u, kc.Name())
155+
result, err := r.Runner.Run(ident, u, kc.Name())
145156
if err != nil {
146157
return reconcileResult, err
147158
}
148159

149160
// iterate events from ansible, looking for the final one
150161
statusEvent := eventapi.StatusJobEvent{}
151162
failureMessages := eventapi.FailureMessages{}
152-
for event := range eventChan {
163+
for event := range result.Events {
153164
for _, eHandler := range r.EventHandlers {
154-
go eHandler.Handle(u, event)
165+
go eHandler.Handle(ident, u, event)
155166
}
156167
if event.Event == eventapi.EventPlaybookOnStats {
157168
// convert to StatusJobEvent; would love a better way to do this
@@ -169,9 +180,15 @@ func (r *AnsibleOperatorReconciler) Reconcile(request reconcile.Request) (reconc
169180
}
170181
}
171182
if statusEvent.Event == "" {
172-
err := errors.New("did not receive playbook_on_stats event")
173-
logrus.Error(err.Error())
174-
return reconcileResult, err
183+
msg := "did not receive playbook_on_stats event"
184+
logger.Error(msg)
185+
stdout, err := result.Stdout()
186+
if err != nil {
187+
logger.Infof("failed to get ansible-runner stdout: %s\n", err.Error())
188+
} else {
189+
logger.Error(stdout)
190+
}
191+
return reconcileResult, errors.New(msg)
175192
}
176193

177194
// We only want to update the CustomResource once, so we'll track changes and do it at the end

pkg/ansible/events/log_events.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,21 @@ const (
3636

3737
// EventHandler - knows how to handle job events.
3838
type EventHandler interface {
39-
Handle(*unstructured.Unstructured, eventapi.JobEvent)
39+
Handle(string, *unstructured.Unstructured, eventapi.JobEvent)
4040
}
4141

4242
type loggingEventHandler struct {
4343
LogLevel LogLevel
4444
}
4545

46-
func (l loggingEventHandler) Handle(u *unstructured.Unstructured, e eventapi.JobEvent) {
46+
func (l loggingEventHandler) Handle(ident string, u *unstructured.Unstructured, e eventapi.JobEvent) {
4747
log := logrus.WithFields(logrus.Fields{
4848
"component": "logging_event_handler",
4949
"name": u.GetName(),
5050
"namespace": u.GetNamespace(),
5151
"gvk": u.GroupVersionKind().String(),
5252
"event_type": e.Event,
53+
"job": ident,
5354
})
5455
if l.LogLevel == Nothing {
5556
return
@@ -71,6 +72,10 @@ func (l loggingEventHandler) Handle(u *unstructured.Unstructured, e eventapi.Job
7172
if e.Event == eventapi.EventRunnerOnFailed {
7273
log.Errorf("[failed]: [playbook task] '%s' failed with task_args - %v",
7374
t, e.EventData["task_args"])
75+
taskPath, ok := e.EventData["task_path"]
76+
if ok {
77+
log.Errorf("failed task: %s\n", taskPath)
78+
}
7479
return
7580
}
7681
}

pkg/ansible/runner/eventapi/eventapi.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func (e *EventReceiver) handleEvents(w http.ResponseWriter, r *http.Request) {
165165
// we're not currently interested in.
166166
// https://ansible-runner.readthedocs.io/en/latest/external_interface.html#event-structure
167167
if event.UUID == "" {
168-
e.logger.Info("dropping event that is not a JobEvent")
168+
e.logger.Debug("dropping event that is not a JobEvent")
169169
} else {
170170
// timeout if the channel blocks for too long
171171
timeout := time.NewTimer(10 * time.Second)

pkg/ansible/runner/internal/inputdir/inputdir.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ func (i *InputDir) addFile(path string, content []byte) error {
5656
return err
5757
}
5858

59+
// Stdout reads the stdout from the ansible artifact that corresponds to the
60+
// given ident and returns it as a string.
61+
func (i *InputDir) Stdout(ident string) (string, error) {
62+
errorPath := filepath.Join(i.Path, "artifacts", ident, "stdout")
63+
errorText, err := ioutil.ReadFile(errorPath)
64+
return string(errorText), err
65+
}
66+
5967
// Write commits the object's state to the filesystem at i.Path.
6068
func (i *InputDir) Write() error {
6169
paramBytes, err := json.Marshal(i.Parameters)

pkg/ansible/runner/runner.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ import (
1818
"errors"
1919
"fmt"
2020
"io/ioutil"
21-
"math/rand"
2221
"net/http"
2322
"os"
2423
"os/exec"
2524
"path/filepath"
26-
"strconv"
2725
"strings"
2826
"time"
2927

@@ -39,7 +37,7 @@ import (
3937
// Runner - a runnable that should take the parameters and name and namespace
4038
// and run the correct code.
4139
type Runner interface {
42-
Run(*unstructured.Unstructured, string) (chan eventapi.JobEvent, error)
40+
Run(string, *unstructured.Unstructured, string) (*RunResult, error)
4341
GetFinalizer() (string, bool)
4442
GetReconcilePeriod() (time.Duration, bool)
4543
}
@@ -176,11 +174,10 @@ type runner struct {
176174
reconcilePeriod time.Duration
177175
}
178176

179-
func (r *runner) Run(u *unstructured.Unstructured, kubeconfig string) (chan eventapi.JobEvent, error) {
177+
func (r *runner) Run(ident string, u *unstructured.Unstructured, kubeconfig string) (*RunResult, error) {
180178
if u.GetDeletionTimestamp() != nil && !r.isFinalizerRun(u) {
181179
return nil, errors.New("resource has been deleted, but no finalizer was matched, skipping reconciliation")
182180
}
183-
ident := strconv.Itoa(rand.Int())
184181
logger := logrus.WithFields(logrus.Fields{
185182
"component": "runner",
186183
"job": ident,
@@ -242,7 +239,11 @@ func (r *runner) Run(u *unstructured.Unstructured, kubeconfig string) (chan even
242239
logger.Errorf("error from event api: %s", err.Error())
243240
}
244241
}()
245-
return receiver.Events, nil
242+
return &RunResult{
243+
Events: receiver.Events,
244+
inputDir: &inputDir,
245+
ident: ident,
246+
}, nil
246247
}
247248

248249
// GetReconcilePeriod - new reconcile period.
@@ -330,3 +331,18 @@ func (r *runner) makeParameters(u *unstructured.Unstructured) map[string]interfa
330331
}
331332
return parameters
332333
}
334+
335+
// RunResult facilitates access to information about a run of ansible.
336+
type RunResult struct {
337+
// Events is a channel of events from ansible that contain state related
338+
// to a run of ansible.
339+
Events <-chan eventapi.JobEvent
340+
341+
ident string
342+
inputDir *inputdir.InputDir
343+
}
344+
345+
// Stdout returns the stdout from ansible-runner if it is available, else an error.
346+
func (r *RunResult) Stdout() (string, error) {
347+
return r.inputDir.Stdout(r.ident)
348+
}

0 commit comments

Comments
 (0)