Skip to content

Commit 6859fb8

Browse files
committed
Emit events for all TaskRun lifecycle events
Start emitting events for additional TaskRun lifecyle events: - taskrun started - taskrun timeout Introduce pre-run and post-run functions that are invoked asynchronously when the taskrun starts and completes, to emit events. These same functions shall be used to trigger any other async behaviour on start/stop of taskruns. Add documentation on events. Fixes #2328 Work towards #2082
1 parent 94354a2 commit 6859fb8

File tree

8 files changed

+95
-110
lines changed

8 files changed

+95
-110
lines changed

docs/events.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!--
2+
---
3+
linkTitle: "Events"
4+
weight: 2
5+
---
6+
-->
7+
# Events
8+
9+
Tekton runtime resources, specifically `TaskRuns` and `PipelineRuns`,
10+
emit events when they are executed, so that users can monitor their lifecycle
11+
and react to it. Tekton emits [kubernetes events](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#event-v1-core), that can be retrieve from the resource via
12+
`kubectl describe [resource]`.
13+
14+
No events are emitted for `Conditions` today.
15+
16+
## TaskRuns
17+
18+
`TaskRun` events are generated for the following `Reasons`:
19+
- `Started`: this is triggered the first time the `TaskRun` is picked by the
20+
reconciler from its work queue, so it only happens if web-hook validation was
21+
successful. Note that this event does not imply that a step started executing,
22+
as several conditions must be met first:
23+
- task and bound resource validation must be successful
24+
- attached conditions must run successfully
25+
- the `Pod` associated to the `TaskRun` must be successfully scheduled
26+
- `Succeeded`: this is triggered once all steps in the `TaskRun` are executed
27+
successfully, including post-steps injected by Tekton.
28+
- `Failed`: this is triggered if the `TaskRun` is completed, but not successfully.
29+
Causes of failure may be: one the steps failed, the `TaskRun` was cancelled or
30+
the `TaskRun` timed out.
31+
32+
## PipelineRuns
33+
34+
`PipelineRun` events are generated for the following `Reasons`:
35+
- `Succeeded`: this is triggered once all `Tasks` reachable via the DAG are
36+
executed successfully.
37+
- `Failed`: this is triggered if the `PipelineRun` is completed, but not
38+
successfully. Causes of failure may be: one the `Tasks` failed or the
39+
`PipelineRun` was cancelled.

docs/pipelineruns.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Creation of a `PipelineRun` will trigger the creation of
2929
- [Workspaces](#workspaces)
3030
- [Cancelling a PipelineRun](#cancelling-a-pipelinerun)
3131
- [LimitRanges](#limitranges)
32+
- [Events](events.md#pipelineruns)
3233

3334
## Syntax
3435

docs/taskruns.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ A `TaskRun` runs until all `steps` have completed or until a failure occurs.
3030
- [Steps](#steps)
3131
- [Results](#results)
3232
- [Cancelling a TaskRun](#cancelling-a-taskrun)
33+
- [Sidecars](#sidecars)
34+
- [LimitRanges](#limitranges)
35+
- [Events](events.md#taskruns)
3336
- [Examples](#examples)
3437
- [Example TaskRun](#example-taskrun)
3538
- [Example with embedded specs](#example-with-embedded-specs)
3639
- [Example Task Reuse](#example-task-reuse)
3740
- [Using a `ServiceAccount`](#using-a-serviceaccount)
38-
- [Sidecars](#sidecars)
39-
- [LimitRanges](#limitranges)
40-
4141
---
4242

4343
## Syntax

pkg/reconciler/event.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func EmitEvent(c record.EventRecorder, beforeCondition *apis.Condition, afterCon
3131
c.Event(object, corev1.EventTypeNormal, "Succeeded", afterCondition.Message)
3232
} else if afterCondition.Status == corev1.ConditionFalse {
3333
c.Event(object, corev1.EventTypeWarning, "Failed", afterCondition.Message)
34+
} else {
35+
if beforeCondition == nil {
36+
c.Event(object, corev1.EventTypeNormal, "Started", "")
37+
}
3438
}
3539
}
3640
}

pkg/reconciler/event_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ func TestEmitEvent(t *testing.T) {
8080
Status: corev1.ConditionTrue,
8181
},
8282
expectEvent: true,
83+
}, {
84+
name: "nil to unknown",
85+
before: nil,
86+
after: &apis.Condition{
87+
Type: apis.ConditionSucceeded,
88+
Status: corev1.ConditionUnknown,
89+
},
90+
expectEvent: true,
8391
}}
8492

8593
for _, ts := range testcases {

pkg/reconciler/taskrun/cancel.go

Lines changed: 0 additions & 77 deletions
This file was deleted.

pkg/reconciler/taskrun/resources/cloudevent/cloud_event_controller.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ func cloudEventDeliveryFromTargets(targets []string) []v1alpha1.CloudEventDelive
6666
}
6767

6868
// SendCloudEvents is used by the TaskRun controller to send cloud events once
69-
// the TaskRun is complete. `tr` is used to obtain the list of targets but also
70-
// to construct the body of the
69+
// the TaskRun is complete. `tr` is used to obtain the list of targets
7170
func SendCloudEvents(tr *v1alpha1.TaskRun, ceclient CEClient, logger *zap.SugaredLogger) error {
7271
logger = logger.With(zap.String("taskrun", tr.Name))
7372

pkg/reconciler/taskrun/taskrun.go

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,15 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
106106

107107
// If the TaskRun is just starting, this will also set the starttime,
108108
// from which the timeout will immediately begin counting down.
109-
tr.Status.InitializeConditions()
110-
// In case node time was not synchronized, when controller has been scheduled to other nodes.
111-
if tr.Status.StartTime.Sub(tr.CreationTimestamp.Time) < 0 {
112-
c.Logger.Warnf("TaskRun %s createTimestamp %s is after the taskRun started %s", tr.GetRunKey(), tr.CreationTimestamp, tr.Status.StartTime)
113-
tr.Status.StartTime = &tr.CreationTimestamp
109+
if !tr.HasStarted() {
110+
tr.Status.InitializeConditions()
111+
// In case node time was not synchronized, when controller has been scheduled to other nodes.
112+
if tr.Status.StartTime.Sub(tr.CreationTimestamp.Time) < 0 {
113+
c.Logger.Warnf("TaskRun %s createTimestamp %s is after the taskRun started %s", tr.GetRunKey(), tr.CreationTimestamp, tr.Status.StartTime)
114+
tr.Status.StartTime = &tr.CreationTimestamp
115+
}
116+
// Run asnyc startup hooks
117+
go c.preRunAsyncHook(ctx, tr)
114118
}
115119

116120
// If the TaskRun is complete, run some post run fixtures when applicable
@@ -164,36 +168,20 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
164168
// If the TaskRun is cancelled, kill resources and update status
165169
if tr.IsCancelled() {
166170
before := tr.Status.GetCondition(apis.ConditionSucceeded)
167-
<<<<<<< HEAD
168171
message := fmt.Sprintf("TaskRun %q was cancelled", tr.Name)
169172
err := c.failTaskRun(tr, v1beta1.TaskRunReasonCancelled, message)
170-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
171-
reconciler.EmitEvent(c.Recorder, before, after, tr)
173+
go c.postRunAsyncHook(ctx, tr, before)
172174
return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
173-
=======
174-
err := cancelTaskRun(tr, c.KubeClientSet, c.Logger)
175-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
176-
reconciler.EmitEvent(c.Recorder, before, after, tr)
177-
return err
178-
>>>>>>> Consolidate cancel and timeout logic
179175
}
180176

181177
// Check if the TaskRun has timed out; if it is, this will set its status
182178
// accordingly.
183179
if tr.HasTimedOut() {
184180
before := tr.Status.GetCondition(apis.ConditionSucceeded)
185-
<<<<<<< HEAD
186181
message := fmt.Sprintf("TaskRun %q failed to finish within %q", tr.Name, tr.GetTimeout())
187182
err := c.failTaskRun(tr, podconvert.ReasonTimedOut, message)
188-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
189-
reconciler.EmitEvent(c.Recorder, before, after, tr)
183+
go c.postRunAsyncHook(ctx, tr, before)
190184
return multierror.Append(err, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
191-
=======
192-
err := timeoutTaskRun(tr, c.KubeClientSet, c.Logger)
193-
after := tr.Status.GetCondition(apis.ConditionSucceeded)
194-
reconciler.EmitEvent(c.Recorder, before, after, tr)
195-
return err
196-
>>>>>>> Consolidate cancel and timeout logic
197185
}
198186

199187
// Reconcile this copy of the task run and then write back any status
@@ -205,15 +193,31 @@ func (c *Reconciler) Reconcile(ctx context.Context, key string) error {
205193
return multierror.Append(merr, c.updateStatusLabelsAndAnnotations(tr, original)).ErrorOrNil()
206194
}
207195

196+
// Run any async logic that may be required at start-up time. This method is used
197+
// to emit events, notifications or any other async operation
198+
func (c *Reconciler) preRunAsyncHook(ctx context.Context, tr *v1alpha1.TaskRun) {
199+
c.Logger.Infof("preRunAsyncHook: %s", tr.Name)
200+
201+
// Emit event
202+
afterCondition := tr.Status.GetCondition(apis.ConditionSucceeded)
203+
reconciler.EmitEvent(c.Recorder, nil, afterCondition, tr)
204+
}
205+
206+
// Run any async logic that may be required once the tr is successfully reconciled
207+
// This method is used to emit events, notifications or any other async operation
208+
func (c *Reconciler) postRunAsyncHook(ctx context.Context, tr *v1alpha1.TaskRun, beforeCondition *apis.Condition) {
209+
c.Logger.Infof("postRunAsyncHook: %s", tr.Name)
210+
211+
// Emit event
212+
afterCondition := tr.Status.GetCondition(apis.ConditionSucceeded)
213+
reconciler.EmitEvent(c.Recorder, beforeCondition, afterCondition, tr)
214+
}
215+
208216
func (c *Reconciler) reconcile(ctx context.Context, tr *v1alpha1.TaskRun) error {
209217
// We may be reading a version of the object that was stored at an older version
210218
// and may not have had all of the assumed default specified.
211219
tr.SetDefaults(contexts.WithUpgradeViaDefaulting(ctx))
212220

213-
if tr.Spec.Timeout == nil {
214-
tr.Spec.Timeout = &metav1.Duration{Duration: config.DefaultTimeoutMinutes * time.Minute}
215-
}
216-
217221
if err := tr.ConvertTo(ctx, &v1beta1.TaskRun{}); err != nil {
218222
if ce, ok := err.(*v1beta1.CannotConvertError); ok {
219223
tr.Status.MarkResourceNotConvertible(ce)
@@ -366,7 +370,14 @@ func (c *Reconciler) reconcile(ctx context.Context, tr *v1alpha1.TaskRun) error
366370

367371
after := tr.Status.GetCondition(apis.ConditionSucceeded)
368372

369-
reconciler.EmitEvent(c.Recorder, before, after, tr)
373+
// If after is different from before and status is not Unknown, the taskrun
374+
// has completed its work - except for post-run tasks like emitting events,
375+
// recording metrics, sending cloud events.
376+
// Once tr.isDone becomes true, even when this key is queued, `reconcile`
377+
// won't be invoked so we won't pass through here again
378+
if tr.IsDone() && after != before {
379+
go c.postRunAsyncHook(ctx, tr, before)
380+
}
370381
c.Logger.Infof("Successfully reconciled taskrun %s/%s with status: %#v", tr.Name, tr.Namespace, after)
371382

372383
return nil

0 commit comments

Comments
 (0)