Skip to content

Commit 1f411f8

Browse files
committed
Emit events when we fail to update the taskrun
Similarly to how the pipeline controller does, we should emit events when we fail to update the taskrun. Events on reconcile error use a dedicated reason.
1 parent 7bbc7eb commit 1f411f8

File tree

4 files changed

+94
-55
lines changed

4 files changed

+94
-55
lines changed

pkg/reconciler/event.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ const (
3030
EventReasonFailed = "Failed"
3131
// EventReasonStarted is the reason set for events about the start of TaskRuns / PipelineRuns
3232
EventReasonStarted = "Started"
33+
// EventReasonError is the reason set for events related to TaskRuns / PipelineRuns reconcile errors
34+
EventReasonError = "Error"
3335
)
3436

3537
// EmitEvent emits an event for object if afterCondition is different from beforeCondition
@@ -63,3 +65,10 @@ func EmitEvent(c record.EventRecorder, beforeCondition *apis.Condition, afterCon
6365
}
6466
}
6567
}
68+
69+
// EmitErrorEvent emits a failure associated to an error
70+
func EmitErrorEvent(c record.EventRecorder, err error, object runtime.Object) {
71+
if err != nil {
72+
c.Event(object, corev1.EventTypeWarning, EventReasonError, err.Error())
73+
}
74+
}

pkg/reconciler/event_test.go

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ limitations under the License.
1717
package reconciler
1818

1919
import (
20+
"errors"
21+
"fmt"
22+
"strings"
2023
"testing"
2124
"time"
2225

@@ -28,11 +31,10 @@ import (
2831

2932
func TestEmitEvent(t *testing.T) {
3033
testcases := []struct {
31-
name string
32-
before *apis.Condition
33-
after *apis.Condition
34-
expectEvent bool
35-
expectedEvent string
34+
name string
35+
before *apis.Condition
36+
after *apis.Condition
37+
wantEvent string
3638
}{{
3739
name: "unknown to true with message",
3840
before: &apis.Condition{
@@ -44,8 +46,7 @@ func TestEmitEvent(t *testing.T) {
4446
Status: corev1.ConditionTrue,
4547
Message: "all done",
4648
},
47-
expectEvent: true,
48-
expectedEvent: "Normal Succeeded all done",
49+
wantEvent: "Normal Succeeded all done",
4950
}, {
5051
name: "true to true",
5152
before: &apis.Condition{
@@ -58,8 +59,7 @@ func TestEmitEvent(t *testing.T) {
5859
Status: corev1.ConditionTrue,
5960
LastTransitionTime: apis.VolatileTime{Inner: metav1.NewTime(time.Now().Add(5 * time.Minute))},
6061
},
61-
expectEvent: false,
62-
expectedEvent: "",
62+
wantEvent: "",
6363
}, {
6464
name: "false to false",
6565
before: &apis.Condition{
@@ -70,8 +70,7 @@ func TestEmitEvent(t *testing.T) {
7070
Type: apis.ConditionSucceeded,
7171
Status: corev1.ConditionFalse,
7272
},
73-
expectEvent: false,
74-
expectedEvent: "",
73+
wantEvent: "",
7574
}, {
7675
name: "unknown to unknown",
7776
before: &apis.Condition{
@@ -86,26 +85,23 @@ func TestEmitEvent(t *testing.T) {
8685
Reason: "foo",
8786
Message: "bar",
8887
},
89-
expectEvent: true,
90-
expectedEvent: "Normal foo bar",
88+
wantEvent: "Normal foo bar",
9189
}, {
9290
name: "true to nil",
9391
after: nil,
9492
before: &apis.Condition{
9593
Type: apis.ConditionSucceeded,
9694
Status: corev1.ConditionTrue,
9795
},
98-
expectEvent: false,
99-
expectedEvent: "",
96+
wantEvent: "",
10097
}, {
10198
name: "nil to true",
10299
before: nil,
103100
after: &apis.Condition{
104101
Type: apis.ConditionSucceeded,
105102
Status: corev1.ConditionTrue,
106103
},
107-
expectEvent: true,
108-
expectedEvent: "Normal Succeeded ",
104+
wantEvent: "Normal Succeeded ",
109105
}, {
110106
name: "nil to unknown with message",
111107
before: nil,
@@ -114,8 +110,7 @@ func TestEmitEvent(t *testing.T) {
114110
Status: corev1.ConditionUnknown,
115111
Message: "just starting",
116112
},
117-
expectEvent: true,
118-
expectedEvent: "Normal Started ",
113+
wantEvent: "Normal Started ",
119114
}, {
120115
name: "unknown to false with message",
121116
before: &apis.Condition{
@@ -127,43 +122,70 @@ func TestEmitEvent(t *testing.T) {
127122
Status: corev1.ConditionFalse,
128123
Message: "really bad",
129124
},
130-
expectEvent: true,
131-
expectedEvent: "Warning Failed really bad",
125+
wantEvent: "Warning Failed really bad",
132126
}, {
133127
name: "nil to false",
134128
before: nil,
135129
after: &apis.Condition{
136130
Type: apis.ConditionSucceeded,
137131
Status: corev1.ConditionFalse,
138132
},
139-
expectEvent: true,
140-
expectedEvent: "Warning Failed ",
133+
wantEvent: "Warning Failed ",
141134
}}
142135

143136
for _, ts := range testcases {
144137
fr := record.NewFakeRecorder(1)
145138
tr := &corev1.Pod{}
146139
EmitEvent(fr, ts.before, ts.after, tr)
147-
timer := time.NewTimer(1 * time.Second)
148140

149-
select {
150-
case event := <-fr.Events:
151-
if event == "" {
152-
// The fake recorder reported empty, it should not happen
153-
t.Fatalf("Expected event but got empty for %s", ts.name)
154-
}
155-
if !ts.expectEvent {
156-
// The fake recorder reported an event which we did not expect
157-
t.Errorf("Unxpected event \"%s\" but got one for %s", event, ts.name)
158-
}
159-
if !(event == ts.expectedEvent) {
160-
t.Errorf("Expected event \"%s\" but got \"%s\" instead for %s", ts.expectedEvent, event, ts.name)
161-
}
162-
case <-timer.C:
163-
if ts.expectEvent {
164-
// The fake recorder did not report, the timer timeout expired
165-
t.Errorf("Expected event but got none for %s", ts.name)
166-
}
141+
err := checkEvents(fr, ts.name, ts.wantEvent)
142+
if err != nil {
143+
t.Errorf(err.Error())
167144
}
168145
}
169146
}
147+
148+
func TestEmitErrorEvent(t *testing.T) {
149+
testcases := []struct {
150+
name string
151+
err error
152+
wantEvent string
153+
}{{
154+
name: "with error",
155+
err: errors.New("something went wrong"),
156+
wantEvent: "Warning Error something went wrong",
157+
}, {
158+
name: "without error",
159+
err: nil,
160+
wantEvent: "",
161+
}}
162+
163+
for _, ts := range testcases {
164+
fr := record.NewFakeRecorder(1)
165+
tr := &corev1.Pod{}
166+
EmitErrorEvent(fr, ts.err, tr)
167+
168+
err := checkEvents(fr, ts.name, ts.wantEvent)
169+
if err != nil {
170+
t.Errorf(err.Error())
171+
}
172+
}
173+
}
174+
175+
func checkEvents(fr *record.FakeRecorder, testName string, wantEvent string) error {
176+
timer := time.NewTimer(1 * time.Second)
177+
select {
178+
case event := <-fr.Events:
179+
if wantEvent == "" {
180+
return fmt.Errorf("received event \"%s\" for %s but none expected", event, testName)
181+
}
182+
if !(strings.HasPrefix(event, wantEvent)) {
183+
return fmt.Errorf("expected event \"%s\" but got \"%s\" instead for %s", wantEvent, event, testName)
184+
}
185+
case <-timer.C:
186+
if wantEvent != "" {
187+
return fmt.Errorf("received no events for %s but %s expected", testName, wantEvent)
188+
}
189+
}
190+
return nil
191+
}

pkg/reconciler/taskrun/taskrun.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
168168
before := tr.Status.GetCondition(apis.ConditionSucceeded)
169169
message := fmt.Sprintf("TaskRun %q was cancelled", tr.Name)
170170
err := c.failTaskRun(tr, v1beta1.TaskRunReasonCancelled, message)
171-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
172-
reconciler.EmitEvent(c.Recorder, before, after, tr)
173-
return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
171+
return c.finishReconcileUpdateEmitEvents(tr, original, before, err)
174172
}
175173

176174
// Check if the TaskRun has timed out; if it is, this will set its status
@@ -179,9 +177,7 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
179177
before := tr.Status.GetCondition(apis.ConditionSucceeded)
180178
message := fmt.Sprintf("TaskRun %q failed to finish within %q", tr.Name, tr.GetTimeout())
181179
err := c.failTaskRun(tr, podconvert.ReasonTimedOut, message)
182-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
183-
reconciler.EmitEvent(c.Recorder, before, after, tr)
184-
return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
180+
return c.finishReconcileUpdateEmitEvents(tr, original, before, err)
185181
}
186182

187183
// prepare fetches all required resources, validates them together with the
@@ -190,11 +186,9 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
190186
taskSpec, rtr, err := c.prepare(ctx, tr)
191187
if err != nil {
192188
c.Logger.Errorf("TaskRun prepare error: %v", err.Error())
193-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
194-
reconciler.EmitEvent(c.Recorder, nil, after, tr)
195189
// We only return an error if update failed, otherwise we don't want to
196190
// reconcile an invalid TaskRun anymore
197-
return c.updateStatusLabelsAndAnnotations(tr, original)
191+
return c.finishReconcileUpdateEmitEvents(tr, original, nil, nil)
198192
}
199193

200194
// Store the condition before reconcile
@@ -206,10 +200,15 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
206200
c.Logger.Errorf("Reconcile error: %v", err.Error())
207201
}
208202
// Emit events (only when ConditionSucceeded was changed)
209-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
210-
reconciler.EmitEvent(c.Recorder, before, after, tr)
203+
return c.finishReconcileUpdateEmitEvents(tr, original, before, err)
204+
}
211205

212-
return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
206+
func (c *Reconciler) finishReconcileUpdateEmitEvents(tr, original *v1alpha1.TaskRun, beforeCondition *apis.Condition, previousError error) error {
207+
afterCondition := tr.Status.GetCondition(apis.ConditionSucceeded)
208+
reconciler.EmitEvent(c.Recorder, beforeCondition, afterCondition, tr)
209+
err := c.updateStatusLabelsAndAnnotations(tr, original)
210+
reconciler.EmitErrorEvent(c.Recorder, err, tr)
211+
return multierror.Append(previousError, err).ErrorOrNil()
213212
}
214213

215214
func (c *Reconciler) getTaskResolver(tr *v1alpha1.TaskRun) (*resources.LocalTaskRefResolver, v1alpha1.TaskKind) {

test/cancel_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ package test
2020

2121
import (
2222
"encoding/json"
23+
"fmt"
2324
"sync"
2425
"testing"
2526

@@ -171,14 +172,22 @@ func TestTaskRunPipelineRunCancel(t *testing.T) {
171172
wg.Wait()
172173

173174
matchKinds := map[string][]string{"PipelineRun": {"pear"}, "TaskRun": trName}
175+
// Expected failure events: 1 for the pipelinerun cancel, 1 for each TaskRun
174176
expectedNumberOfEvents := 1 + len(trName)
175177
t.Logf("Making sure %d events were created from pipelinerun with kinds %v", expectedNumberOfEvents, matchKinds)
176178
events, err := collectMatchingEvents(c.KubeClient, namespace, matchKinds, "Failed")
177179
if err != nil {
178180
t.Fatalf("Failed to collect matching events: %q", err)
179181
}
180182
if len(events) != expectedNumberOfEvents {
181-
t.Fatalf("Expected %d number of successful events from pipelinerun and taskrun but got %d; list of received events : %#v", expectedNumberOfEvents, len(events), events)
183+
collectedEvents := ""
184+
for i, event := range events {
185+
collectedEvents += fmt.Sprintf("%#v", event)
186+
if i < (len(events) - 1) {
187+
collectedEvents += ", "
188+
}
189+
}
190+
t.Fatalf("Expected %d number of successful events from pipelinerun and taskrun but got %d; list of received events : %#v", expectedNumberOfEvents, len(events), collectedEvents)
182191
}
183192
})
184193
}

0 commit comments

Comments
 (0)