Skip to content

Commit c101eb1

Browse files
committed
TODO: Delete - just copying over the TestPipelineRunTimeout fix to test it here too
1 parent 6e4302c commit c101eb1

File tree

9 files changed

+611
-16
lines changed

9 files changed

+611
-16
lines changed

pkg/apis/pipeline/v1beta1/taskrun_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ const (
8888
// TaskRunSpecStatusCancelled indicates that the user wants to cancel the task,
8989
// if not already cancelled or terminated
9090
TaskRunSpecStatusCancelled = "TaskRunCancelled"
91+
// TaskRunSpecStatusTimedOut indicates that the PipelineRun owning this task wants to mark it as timed out,
92+
// if not already cancelled or terminated
93+
TaskRunSpecStatusTimedOut = "TaskRunTimedOut"
9194
)
9295

9396
// TaskRunDebug defines the breakpoint config for a particular TaskRun
@@ -424,6 +427,11 @@ func (tr *TaskRun) IsCancelled() bool {
424427
return tr.Spec.Status == TaskRunSpecStatusCancelled
425428
}
426429

430+
// IsSpecTimedOut returns true if the TaskRun's spec status is set to TimedOut state
431+
func (tr *TaskRun) IsSpecTimedOut() bool {
432+
return tr.Spec.Status == TaskRunSpecStatusTimedOut
433+
}
434+
427435
// HasTimedOut returns true if the TaskRun runtime is beyond the allowed timeout
428436
func (tr *TaskRun) HasTimedOut(ctx context.Context, c clock.PassiveClock) bool {
429437
if tr.Status.StartTime.IsZero() {

pkg/apis/pipeline/v1beta1/taskrun_validation.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ func (ts *TaskRunSpec) Validate(ctx context.Context) (errs *apis.FieldError) {
7979
}
8080

8181
if ts.Status != "" {
82-
if ts.Status != TaskRunSpecStatusCancelled {
83-
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be %s", ts.Status, TaskRunSpecStatusCancelled), "status"))
82+
if ts.Status != TaskRunSpecStatusCancelled && ts.Status != TaskRunSpecStatusTimedOut {
83+
errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%s should be %s or %s", ts.Status, TaskRunSpecStatusCancelled, TaskRunSpecStatusTimedOut), "status"))
8484
}
8585
}
8686
if ts.Timeout != nil {

pkg/apis/pipeline/v1beta1/taskrun_validation_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ func TestTaskRunSpec_Invalidate(t *testing.T) {
231231
},
232232
Status: "TaskRunCancell",
233233
},
234-
wantErr: apis.ErrInvalidValue("TaskRunCancell should be TaskRunCancelled", "status"),
234+
wantErr: apis.ErrInvalidValue("TaskRunCancell should be TaskRunCancelled or TaskRunTimedOut", "status"),
235235
}, {
236236
name: "invalid taskspec",
237237
spec: v1beta1.TaskRunSpec{

pkg/reconciler/pipelinerun/pipelinerun.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ const (
109109
// ReasonCouldntCancel indicates that a PipelineRun was cancelled but attempting to update
110110
// all of the running TaskRuns as cancelled failed.
111111
ReasonCouldntCancel = "PipelineRunCouldntCancel"
112+
// ReasonCouldntTimeOut indicates that a PipelineRun was timed out but attempting to update
113+
// all of the running TaskRuns as timed out failed.
114+
ReasonCouldntTimeOut = "PipelineRunCouldntTimeOut"
112115
// ReasonInvalidTaskResultReference indicates a task result was declared
113116
// but was not initialized by that task
114117
ReasonInvalidTaskResultReference = "InvalidTaskResultReference"
@@ -578,6 +581,13 @@ func (c *Reconciler) reconcile(ctx context.Context, pr *v1beta1.PipelineRun, get
578581
// Reset the skipped status to trigger recalculation
579582
pipelineRunFacts.ResetSkippedCache()
580583

584+
// If the pipelinerun has timed out, mark tasks as timed out and update status
585+
if pr.HasTimedOut(ctx, c.Clock) {
586+
if err := timeoutPipelineRun(ctx, logger, pr, c.PipelineClientSet); err != nil {
587+
return err
588+
}
589+
}
590+
581591
after := pipelineRunFacts.GetPipelineConditionStatus(ctx, pr, logger, c.Clock)
582592
switch after.Status {
583593
case corev1.ConditionTrue:
@@ -620,7 +630,7 @@ func (c *Reconciler) processRunTimeouts(ctx context.Context, pr *v1beta1.Pipelin
620630
}
621631
for _, rpt := range pipelineState {
622632
if rpt.IsCustomTask() {
623-
if rpt.Run != nil && !rpt.Run.IsCancelled() && (pr.HasTimedOut(ctx, c.Clock) || (rpt.Run.HasTimedOut(c.Clock) && !rpt.Run.IsDone())) {
633+
if rpt.Run != nil && !rpt.Run.IsCancelled() && rpt.Run.HasTimedOut(c.Clock) && !rpt.Run.IsDone() {
624634
logger.Infof("Cancelling run task: %s due to timeout.", rpt.RunName)
625635
err := cancelRun(ctx, rpt.RunName, pr.Namespace, c.PipelineClientSet)
626636
if err != nil {
@@ -1140,12 +1150,10 @@ func calculateTaskRunTimeout(timeout time.Duration, pr *v1beta1.PipelineRun, rpt
11401150
if pElapsedTime > timeout {
11411151
return &metav1.Duration{Duration: 1 * time.Second}
11421152
}
1143-
timeRemaining := (timeout - pElapsedTime)
1144-
// Return the smaller of timeRemaining and rpt.pipelineTask.timeout
1145-
if rpt.PipelineTask.Timeout != nil && rpt.PipelineTask.Timeout.Duration < timeRemaining {
1153+
if rpt.PipelineTask.Timeout != nil && rpt.PipelineTask.Timeout.Duration < timeout {
11461154
return &metav1.Duration{Duration: rpt.PipelineTask.Timeout.Duration}
11471155
}
1148-
return &metav1.Duration{Duration: timeRemaining}
1156+
return &metav1.Duration{Duration: timeout}
11491157
}
11501158

11511159
if timeout == apisconfig.NoTimeoutDuration && rpt.PipelineTask.Timeout != nil {

pkg/reconciler/pipelinerun/pipelinerun_test.go

Lines changed: 150 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,16 +1736,44 @@ spec:
17361736
name: test-pipeline
17371737
serviceAccountName: test-sa
17381738
status:
1739+
conditions:
1740+
- message: running...
1741+
reason: Running
1742+
status: Unknown
1743+
type: Succeeded
17391744
startTime: "2021-12-31T00:00:00Z"
1745+
runs:
1746+
test-pipeline-run-custom-task-hello-world-1:
1747+
pipelineTaskName: hello-world-1
1748+
status:
1749+
conditions:
1750+
- status: Unknown
1751+
type: Succeeded
17401752
`)}
17411753
prs[0].Spec.Timeout = tc.timeout
17421754
prs[0].Spec.Timeouts = tc.timeouts
17431755

1756+
runs := []*v1alpha1.Run{mustParseRunWithObjectMeta(t,
1757+
taskRunObjectMeta("test-pipeline-run-custom-task-hello-world-1", "test", "test-pipeline-run-custom-task",
1758+
"test-pipeline", "hello-world-1", true),
1759+
`
1760+
spec:
1761+
ref:
1762+
apiVersion: example.dev/v0
1763+
kind: Example
1764+
status:
1765+
conditions:
1766+
- status: Unknown
1767+
type: Succeeded
1768+
startTime: "2021-12-31T11:58:59Z"
1769+
`)}
1770+
17441771
cms := []*corev1.ConfigMap{withCustomTasks(newFeatureFlagsConfigMap())}
17451772
d := test.Data{
17461773
PipelineRuns: prs,
17471774
Pipelines: ps,
17481775
ConfigMaps: cms,
1776+
Runs: runs,
17491777
}
17501778
prt := newPipelineRunTest(d, t)
17511779
defer prt.Cancel()
@@ -1763,9 +1791,9 @@ status:
17631791
}
17641792

17651793
gotTimeoutValue := postReconcileRun.GetTimeout()
1766-
expectedTimeoutValue := time.Second
1794+
expectedTimeoutValue := time.Hour
17671795

1768-
if d := cmp.Diff(gotTimeoutValue, expectedTimeoutValue); d != "" {
1796+
if d := cmp.Diff(expectedTimeoutValue, gotTimeoutValue); d != "" {
17691797
t.Fatalf("Expected timeout for created Run, but got a mismatch %s", diff.PrintWantGot(d))
17701798
}
17711799

@@ -2755,6 +2783,120 @@ spec:
27552783
}
27562784
}
27572785

2786+
func TestReconcileFailsTaskRunTimeOut(t *testing.T) {
2787+
prName := "test-pipeline-fails-to-timeout"
2788+
2789+
// TestReconcileFailsTaskRunTimeOut runs "Reconcile" on a PipelineRun with a single TaskRun.
2790+
// The TaskRun cannot be timed out. Check that the pipelinerun timeout fails, that reconcile fails and
2791+
// an event is generated
2792+
names.TestingSeed()
2793+
prs := []*v1beta1.PipelineRun{parse.MustParsePipelineRun(t, `
2794+
metadata:
2795+
name: test-pipeline-fails-to-timeout
2796+
namespace: foo
2797+
spec:
2798+
pipelineRef:
2799+
name: test-pipeline
2800+
timeout: 1h0m0s
2801+
status:
2802+
conditions:
2803+
- message: running...
2804+
reason: Running
2805+
status: Unknown
2806+
type: Succeeded
2807+
startTime: "2021-12-31T22:59:00Z"
2808+
taskRuns:
2809+
test-pipeline-fails-to-timeouthello-world-1:
2810+
pipelineTaskName: hello-world-1
2811+
`)}
2812+
ps := []*v1beta1.Pipeline{parse.MustParsePipeline(t, `
2813+
metadata:
2814+
name: test-pipeline
2815+
namespace: foo
2816+
spec:
2817+
tasks:
2818+
- name: hello-world-1
2819+
taskRef:
2820+
name: hello-world
2821+
- name: hello-world-2
2822+
taskRef:
2823+
name: hello-world
2824+
`)}
2825+
tasks := []*v1beta1.Task{simpleHelloWorldTask}
2826+
taskRuns := []*v1beta1.TaskRun{
2827+
getTaskRun(
2828+
t,
2829+
"test-pipeline-fails-to-timeouthello-world-1",
2830+
prName,
2831+
"test-pipeline",
2832+
"hello-world",
2833+
corev1.ConditionUnknown,
2834+
),
2835+
}
2836+
2837+
cms := []*corev1.ConfigMap{withEnabledAlphaAPIFields(newFeatureFlagsConfigMap())}
2838+
2839+
d := test.Data{
2840+
PipelineRuns: prs,
2841+
Pipelines: ps,
2842+
Tasks: tasks,
2843+
TaskRuns: taskRuns,
2844+
ConfigMaps: cms,
2845+
}
2846+
2847+
testAssets, cancel := getPipelineRunController(t, d)
2848+
defer cancel()
2849+
c := testAssets.Controller
2850+
clients := testAssets.Clients
2851+
failingReactorActivated := true
2852+
2853+
// Make the patch call fail, i.e. make it so that the controller fails to cancel the TaskRun
2854+
clients.Pipeline.PrependReactor("patch", "taskruns", func(action ktesting.Action) (bool, runtime.Object, error) {
2855+
return failingReactorActivated, nil, fmt.Errorf("i'm sorry Dave, i'm afraid i can't do that")
2856+
})
2857+
2858+
err := c.Reconciler.Reconcile(testAssets.Ctx, "foo/test-pipeline-fails-to-timeout")
2859+
if err == nil {
2860+
t.Errorf("Expected to see error returned from reconcile after failing to timeout TaskRun but saw none!")
2861+
}
2862+
2863+
// Check that the PipelineRun is still running with correct error message
2864+
reconciledRun, err := clients.Pipeline.TektonV1beta1().PipelineRuns("foo").Get(testAssets.Ctx, "test-pipeline-fails-to-timeout", metav1.GetOptions{})
2865+
if err != nil {
2866+
t.Fatalf("Somehow had error getting reconciled run out of fake client: %s", err)
2867+
}
2868+
2869+
if val, ok := reconciledRun.GetLabels()[pipeline.PipelineLabelKey]; !ok {
2870+
t.Fatalf("expected pipeline label")
2871+
} else if d := cmp.Diff("test-pipeline", val); d != "" {
2872+
t.Errorf("expected to see pipeline label. Diff %s", diff.PrintWantGot(d))
2873+
}
2874+
2875+
// The PipelineRun should not be timed out b/c we couldn't timeout the TaskRun
2876+
checkPipelineRunConditionStatusAndReason(t, reconciledRun, corev1.ConditionUnknown, ReasonCouldntTimeOut)
2877+
// The event here is "Normal" because in case we fail to timeout we leave the condition to unknown
2878+
// Further reconcile might converge then the status of the pipeline.
2879+
// See https://github.com/tektoncd/pipeline/issues/2647 for further details.
2880+
wantEvents := []string{
2881+
"Normal PipelineRunCouldntTimeOut PipelineRun \"test-pipeline-fails-to-timeout\" was timed out but had errors trying to time out TaskRuns and/or Runs",
2882+
"Warning InternalError 1 error occurred",
2883+
}
2884+
err = eventstest.CheckEventsOrdered(t, testAssets.Recorder.Events, prName, wantEvents)
2885+
if err != nil {
2886+
t.Errorf(err.Error())
2887+
}
2888+
2889+
// Turn off failing reactor and retry reconciliation
2890+
failingReactorActivated = false
2891+
2892+
err = c.Reconciler.Reconcile(testAssets.Ctx, "foo/test-pipeline-fails-to-timeout")
2893+
if err == nil {
2894+
// No error is ok
2895+
} else if ok, _ := controller.IsRequeueKey(err); !ok { // Requeue is also fine.
2896+
t.Errorf("Expected to timeout TaskRun successfully!")
2897+
}
2898+
}
2899+
27582900
func TestReconcilePropagateLabelsAndAnnotations(t *testing.T) {
27592901
names.TestingSeed()
27602902

@@ -3094,7 +3236,7 @@ status:
30943236
reconciledRun, _ := prt.reconcileRun("foo", "test-pipeline-retry-run-with-timeout", []string{}, false)
30953237

30963238
if len(reconciledRun.Status.TaskRuns["hello-world-1"].Status.RetriesStatus) != tc.retries {
3097-
t.Fatalf(" %d retry expected but %d ", tc.retries, len(reconciledRun.Status.TaskRuns["hello-world-1"].Status.RetriesStatus))
3239+
t.Fatalf(" %d retries expected but got %d ", tc.retries, len(reconciledRun.Status.TaskRuns["hello-world-1"].Status.RetriesStatus))
30983240
}
30993241

31003242
if status := reconciledRun.Status.TaskRuns["hello-world-1"].Status.GetCondition(apis.ConditionSucceeded).Status; status != tc.conditionSucceeded {
@@ -3240,7 +3382,7 @@ func TestGetTaskRunTimeout(t *testing.T) {
32403382
},
32413383
},
32423384
},
3243-
expected: &metav1.Duration{Duration: 10 * time.Minute},
3385+
expected: &metav1.Duration{Duration: 20 * time.Minute},
32443386
}, {
32453387
name: "taskrun with elapsed time; task.timeout applies",
32463388
timeoutFields: &v1beta1.TimeoutFields{
@@ -3259,7 +3401,7 @@ func TestGetTaskRunTimeout(t *testing.T) {
32593401
},
32603402
},
32613403
},
3262-
expected: &metav1.Duration{Duration: 10 * time.Minute},
3404+
expected: &metav1.Duration{Duration: 15 * time.Minute},
32633405
}, {
32643406
name: "taskrun with elapsed time; timeouts.pipeline applies",
32653407
timeoutFields: &v1beta1.TimeoutFields{
@@ -3278,7 +3420,7 @@ func TestGetTaskRunTimeout(t *testing.T) {
32783420
},
32793421
},
32803422
},
3281-
expected: &metav1.Duration{Duration: 10 * time.Minute},
3423+
expected: &metav1.Duration{Duration: 15 * time.Minute},
32823424
}}
32833425

32843426
for _, tc := range tcs {
@@ -3296,7 +3438,7 @@ func TestGetTaskRunTimeout(t *testing.T) {
32963438
},
32973439
},
32983440
}
3299-
if d := cmp.Diff(getTaskRunTimeout(context.TODO(), pr, tc.rpt, testClock), tc.expected); d != "" {
3441+
if d := cmp.Diff(tc.expected, getTaskRunTimeout(context.TODO(), pr, tc.rpt, testClock)); d != "" {
33003442
t.Errorf("Unexpected task run timeout. Diff %s", diff.PrintWantGot(d))
33013443
}
33023444
})
@@ -3449,7 +3591,7 @@ func TestGetFinallyTaskRunTimeout(t *testing.T) {
34493591
},
34503592
},
34513593
},
3452-
expected: &metav1.Duration{Duration: 11 * time.Minute},
3594+
expected: &metav1.Duration{Duration: 15 * time.Minute},
34533595
}}
34543596

34553597
for _, tc := range tcs {

0 commit comments

Comments
 (0)