Skip to content

Commit 3548a66

Browse files
committed
Add 2 variables context.pipelineTask.retries and context.task.retry-count
`context.pipelineTask.retries` exposes the total retries of a pipelineTask. `context.task.retry-count` exposes the current number of retry. The reason why the variable isn't `context.pipelineTask.retry-count` is that every task has a retry count, only pipelineTask has retries. Rename function `ApplyPipelineTaskContext` to `ApplyPipelineTaskStateContext` in "pipelinerun/apply.go" to distinguish from `ApplyPipelineTaskContexts`.
1 parent 3dc52df commit 3548a66

File tree

12 files changed

+229
-8
lines changed

12 files changed

+229
-8
lines changed

docs/pipelines.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ weight: 3
2020
- [Guard `Task` execution using `Conditions`](#guard-task-execution-using-conditions)
2121
- [Configuring the failure timeout](#configuring-the-failure-timeout)
2222
- [Using variable substitution](#using-variable-substitution)
23+
- [Using the `retries` and `retry-count` variable substitutions](#using-the-retries-and-retry-count-variable-substitutions)
2324
- [Using `Results`](#using-results)
2425
- [Passing one Task's `Results` into the `Parameters` or `WhenExpressions` of another](#passing-one-tasks-results-into-the-parameters-or-whenexpressions-of-another)
2526
- [Emitting `Results` from a `Pipeline`](#emitting-results-from-a-pipeline)
@@ -548,6 +549,35 @@ performed by the Tekton Controller when a PipelineRun is executed.
548549
See the [complete list of variable substitutions for Pipelines](./variables.md#variables-available-in-a-pipeline)
549550
and the [list of fields that accept substitutions](./variables.md#fields-that-accept-variable-substitutions).
550551
552+
### Using the `retries` and `retry-count` variable substitutions
553+
554+
Tekton supports variable substitution for the [`retries`](#using-the-retries-parameter)
555+
parameter of `PipelineTask`. Variables like `context.pipelineTask.retries` and
556+
`context.task.retry-count` can be injected into the content of a `PipelineTask`.
557+
`context.pipelineTask.retries` will be replaced by `retries` of the `PipelineTask`, while
558+
`context.task.retry-count` will be replaced by current retry number of the `PipelineTask`.
559+
560+
```yaml
561+
params:
562+
- name: pipelineTask-retries
563+
value: "$(context.pipelineTask.retries)"
564+
taskSpec:
565+
params:
566+
- name: pipelineTask-retries
567+
steps:
568+
- image: ubuntu
569+
name: print-if-retries-exhausted
570+
script: |
571+
if [ "$(context.task.retry-count)" == "$(params.pipelineTask-retries)" ]
572+
then
573+
echo "This is the last retry."
574+
fi
575+
exit 1
576+
```
577+
578+
**Note:** Every `PipelineTask` can only access its own `retries` and`retry-count`. These
579+
values aren't accessible for other `PipelineTask`s.
580+
551581
## Using `Results`
552582

553583
Tasks can emit [`Results`](tasks.md#emitting-results) when they execute. A Pipeline can use these

docs/variables.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ For instructions on using variable substitutions see the relevant section of [th
4343
| `context.taskRun.namespace` | The namespace of the `TaskRun` that this `Task` is running in. |
4444
| `context.taskRun.uid` | The uid of the `TaskRun` that this `Task` is running in. |
4545
| `context.task.name` | The name of this `Task`. |
46+
| `context.task.retry-count` | The current retry number of this `Task`. |
47+
| `context.pipelineTask.retries` | The retries of this `PipelineTask`. |
4648

4749
### `PipelineResource` variables available in a `Task`
4850

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: Pipeline
3+
metadata:
4+
name: retry-example
5+
spec:
6+
tasks:
7+
- name: retry-me
8+
retries: 5
9+
params:
10+
- name: pipelineTask-retries
11+
value: "$(context.pipelineTask.retries)"
12+
taskSpec:
13+
params:
14+
- name: pipelineTask-retries
15+
steps:
16+
- image: alpine:3.12.0
17+
script: |
18+
#!/usr/bin/env sh
19+
if [ "$(context.task.retry-count)" == "$(params.pipelineTask-retries)" ]; then
20+
echo "This is the last retry."
21+
exit 0;
22+
fi
23+
echo "The PipelineTask has retried $(context.task.retry-count) times."
24+
exit 1
25+
- name: hello-world
26+
runAfter:
27+
- retry-me
28+
taskSpec:
29+
steps:
30+
- image: busybox
31+
script: |
32+
echo "hello world"
33+
---
34+
apiVersion: tekton.dev/v1beta1
35+
kind: PipelineRun
36+
metadata:
37+
generateName: retry-example-run
38+
spec:
39+
pipelineRef:
40+
name: retry-example
41+
serviceAccountName: 'default'

examples/v1beta1/pipelineruns/using_context_variables.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@ spec:
77
pipelineSpec:
88
tasks:
99
- name: task1
10+
retries: 7
1011
params:
1112
- name: pipeline-uid
1213
value: "$(context.pipelineRun.uid)"
1314
- name: pipeline-name
1415
value: "$(context.pipeline.name)"
1516
- name: pipelineRun-name
1617
value: "$(context.pipelineRun.name)"
18+
- name: pipelineTask-retries
19+
value: "$(context.pipelineTask.retries)"
1720
taskSpec:
1821
params:
1922
- name: pipeline-uid
2023
- name: pipeline-name
2124
- name: pipelineRun-name
25+
- name: pipelineTask-retries
2226
steps:
2327
- image: ubuntu
2428
name: print-uid
@@ -31,4 +35,9 @@ spec:
3135
echo "Task name: $(context.task.name)"
3236
echo "TaskRun name: $(context.taskRun.name)"
3337
echo "Pipeline name from params: $(params.pipeline-name)"
34-
echo "PipelineRun name from params: $(params.pipelineRun-name)"
38+
echo "PipelineRun name from params: $(params.pipelineRun-name)"
39+
- image: ubuntu
40+
name: print-retries
41+
script: |
42+
echo "PipelineTask retries from params: $(params.pipelineTask-retries)"
43+
echo "PipelineTask current retry count: $(context.task.retry-count)"

pkg/apis/pipeline/v1beta1/task_validation.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ func validateTaskContextVariables(steps []Step) *apis.FieldError {
234234
)
235235
taskContextNames := sets.NewString().Insert(
236236
"name",
237+
"retry-count",
237238
)
238239
errs := validateVariables(steps, "context\\.taskRun", taskRunContextNames)
239240
return errs.Also(validateVariables(steps, "context\\.task", taskContextNames))

pkg/apis/pipeline/v1beta1/task_validation_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,19 @@ func TestTaskSpecValidate(t *testing.T) {
279279
hello "$(context.task.name)"`,
280280
}},
281281
},
282+
}, {
283+
name: "valid task retry count context",
284+
fields: fields{
285+
Steps: []v1beta1.Step{{
286+
Container: corev1.Container{
287+
Image: "my-image",
288+
Args: []string{"arg"},
289+
},
290+
Script: `
291+
#!/usr/bin/env bash
292+
retry count "$(context.task.retry-count)"`,
293+
}},
294+
},
282295
}, {
283296
name: "valid taskrun name context",
284297
fields: fields{

pkg/reconciler/pipelinerun/pipelinerun.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ func (c *Reconciler) runNextSchedulableTask(ctx context.Context, pr *v1beta1.Pip
614614
fnextRprts := pipelineRunFacts.GetFinalTasks()
615615
if len(fnextRprts) != 0 {
616616
// apply the runtime context just before creating taskRuns for final tasks in queue
617-
resources.ApplyPipelineTaskContext(fnextRprts, pipelineRunFacts.GetPipelineTaskStatus())
617+
resources.ApplyPipelineTaskStateContext(fnextRprts, pipelineRunFacts.GetPipelineTaskStatus())
618618

619619
// Before creating TaskRun for scheduled final task, check if it's consuming a task result
620620
// Resolve and apply task result wherever applicable, report warning in case resolution fails
@@ -707,6 +707,7 @@ func (c *Reconciler) createTaskRun(ctx context.Context, rprt *resources.Resolved
707707
return c.PipelineClientSet.TektonV1beta1().TaskRuns(pr.Namespace).UpdateStatus(ctx, tr, metav1.UpdateOptions{})
708708
}
709709

710+
rprt.PipelineTask = resources.ApplyPipelineTaskContexts(rprt.PipelineTask)
710711
taskRunSpec := pr.GetTaskRunSpec(rprt.PipelineTask.Name)
711712
tr = &v1beta1.TaskRun{
712713
ObjectMeta: metav1.ObjectMeta{

pkg/reconciler/pipelinerun/pipelinerun_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ func TestReconcile(t *testing.T) {
284284
templatedParam := tb.PipelineTaskParam("templatedparam", "$(inputs.workspace.$(params.rev-param))")
285285
contextRunParam := tb.PipelineTaskParam("contextRunParam", "$(context.pipelineRun.name)")
286286
contextPipelineParam := tb.PipelineTaskParam("contextPipelineParam", "$(context.pipeline.name)")
287+
retriesParam := tb.PipelineTaskParam("contextRetriesParam", "$(context.pipelineTask.retries)")
287288
const pipelineName = "test-pipeline"
288289
ps := []*v1beta1.Pipeline{
289290
tb.Pipeline(pipelineName,
@@ -296,18 +297,19 @@ func TestReconcile(t *testing.T) {
296297
tb.PipelineParamSpec("bar", v1beta1.ParamTypeString),
297298
// unit-test-3 uses runAfter to indicate it should run last
298299
tb.PipelineTask("unit-test-3", "unit-test-task",
299-
funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam,
300+
funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, retriesParam,
300301
tb.RunAfter("unit-test-2"),
301302
tb.PipelineTaskInputResource("workspace", "git-repo"),
302303
tb.PipelineTaskOutputResource("image-to-use", "best-image"),
303304
tb.PipelineTaskOutputResource("workspace", "git-repo"),
304305
),
305306
// unit-test-1 can run right away because it has no dependencies
306307
tb.PipelineTask("unit-test-1", "unit-test-task",
307-
funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam,
308+
funParam, moreFunParam, templatedParam, contextRunParam, contextPipelineParam, retriesParam,
308309
tb.PipelineTaskInputResource("workspace", "git-repo"),
309310
tb.PipelineTaskOutputResource("image-to-use", "best-image"),
310311
tb.PipelineTaskOutputResource("workspace", "git-repo"),
312+
tb.Retries(5),
311313
),
312314
// unit-test-2 uses `from` to indicate it should run after `unit-test-1`
313315
tb.PipelineTask("unit-test-2", "unit-test-followup-task",
@@ -327,7 +329,7 @@ func TestReconcile(t *testing.T) {
327329
ts := []*v1beta1.Task{
328330
tb.Task("unit-test-task", tb.TaskSpec(
329331
tb.TaskParam("foo", v1beta1.ParamTypeString), tb.TaskParam("bar", v1beta1.ParamTypeString), tb.TaskParam("templatedparam", v1beta1.ParamTypeString),
330-
tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString),
332+
tb.TaskParam("contextRunParam", v1beta1.ParamTypeString), tb.TaskParam("contextPipelineParam", v1beta1.ParamTypeString), tb.TaskParam("contextRetriesParam", v1beta1.ParamTypeString),
331333
tb.TaskResources(
332334
tb.TaskResourcesInput("workspace", resourcev1alpha1.PipelineResourceTypeGit),
333335
tb.TaskResourcesOutput("image-to-use", resourcev1alpha1.PipelineResourceTypeImage),
@@ -404,6 +406,7 @@ func TestReconcile(t *testing.T) {
404406
tb.TaskRunParam("templatedparam", "$(inputs.workspace.revision)"),
405407
tb.TaskRunParam("contextRunParam", pipelineRunName),
406408
tb.TaskRunParam("contextPipelineParam", pipelineName),
409+
tb.TaskRunParam("contextRetriesParam", "5"),
407410
tb.TaskRunResources(
408411
tb.TaskRunResourcesInput("workspace", tb.TaskResourceBindingRef("some-repo")),
409412
tb.TaskRunResourcesOutput("image-to-use",

pkg/reconciler/pipelinerun/resources/apply.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package resources
1818

1919
import (
2020
"fmt"
21+
"strconv"
2122
"strings"
2223

2324
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
@@ -68,6 +69,17 @@ func ApplyContexts(spec *v1beta1.PipelineSpec, pipelineName string, pr *v1beta1.
6869
return ApplyReplacements(spec, replacements, map[string][]string{})
6970
}
7071

72+
// ApplyPipelineTaskContexts applies the substitution from $(context.pipelineTask.*) with the specified values.
73+
// Uses "0" as a default if a value is not available.
74+
func ApplyPipelineTaskContexts(pt *v1beta1.PipelineTask) *v1beta1.PipelineTask {
75+
pt = pt.DeepCopy()
76+
replacements := map[string]string{
77+
"context.pipelineTask.retries": strconv.Itoa(pt.Retries),
78+
}
79+
pt.Params = replaceParamValues(pt.Params, replacements, map[string][]string{})
80+
return pt
81+
}
82+
7183
// ApplyTaskResults applies the ResolvedResultRef to each PipelineTask.Params and Pipeline.WhenExpressions in targets
7284
func ApplyTaskResults(targets PipelineRunState, resolvedResultRefs ResolvedResultRefs) {
7385
stringReplacements := resolvedResultRefs.getStringReplacements()
@@ -87,8 +99,8 @@ func ApplyTaskResults(targets PipelineRunState, resolvedResultRefs ResolvedResul
8799
}
88100
}
89101

90-
//ApplyPipelineTaskContext replaces context variables referring to execution status with the specified status
91-
func ApplyPipelineTaskContext(state PipelineRunState, replacements map[string]string) {
102+
//ApplyPipelineTaskStateContext replaces context variables referring to execution status with the specified status
103+
func ApplyPipelineTaskStateContext(state PipelineRunState, replacements map[string]string) {
92104
for _, resolvedPipelineRunTask := range state {
93105
if resolvedPipelineRunTask.PipelineTask != nil {
94106
pipelineTask := resolvedPipelineRunTask.PipelineTask.DeepCopy()

pkg/reconciler/pipelinerun/resources/apply_test.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,51 @@ func TestContext(t *testing.T) {
585585
}
586586
}
587587

588+
func TestApplyPipelineTaskContexts(t *testing.T) {
589+
for _, tc := range []struct {
590+
description string
591+
pt v1beta1.PipelineTask
592+
want v1beta1.PipelineTask
593+
}{{
594+
description: "context retries replacement",
595+
pt: v1beta1.PipelineTask{
596+
Retries: 5,
597+
Params: []v1beta1.Param{{
598+
Name: "retries",
599+
Value: *v1beta1.NewArrayOrString("$(context.pipelineTask.retries)"),
600+
}},
601+
},
602+
want: v1beta1.PipelineTask{
603+
Retries: 5,
604+
Params: []v1beta1.Param{{
605+
Name: "retries",
606+
Value: *v1beta1.NewArrayOrString("5"),
607+
}},
608+
},
609+
}, {
610+
description: "context retries replacement with no defined retries",
611+
pt: v1beta1.PipelineTask{
612+
Params: []v1beta1.Param{{
613+
Name: "retries",
614+
Value: *v1beta1.NewArrayOrString("$(context.pipelineTask.retries)"),
615+
}},
616+
},
617+
want: v1beta1.PipelineTask{
618+
Params: []v1beta1.Param{{
619+
Name: "retries",
620+
Value: *v1beta1.NewArrayOrString("0"),
621+
}},
622+
},
623+
}} {
624+
t.Run(tc.description, func(t *testing.T) {
625+
got := ApplyPipelineTaskContexts(&tc.pt)
626+
if d := cmp.Diff(&tc.want, got); d != "" {
627+
t.Errorf(diff.PrintWantGot(d))
628+
}
629+
})
630+
}
631+
}
632+
588633
func TestApplyWorkspaces(t *testing.T) {
589634
for _, tc := range []struct {
590635
description string
@@ -1107,7 +1152,7 @@ func TestApplyTaskRunContext(t *testing.T) {
11071152
}},
11081153
},
11091154
}}
1110-
ApplyPipelineTaskContext(state, r)
1155+
ApplyPipelineTaskStateContext(state, r)
11111156
if d := cmp.Diff(expectedState, state); d != "" {
11121157
t.Fatalf("ApplyTaskRunContext() %s", diff.PrintWantGot(d))
11131158
}

0 commit comments

Comments
 (0)