Skip to content

Commit 4985acd

Browse files
committed
TEP-0090: Fan Out TaskRuns
[TEP-0090: Matrix][tep-0090] proposed executing a `PipelineTask` in parallel `TaskRuns` and `Runs` with substitutions from combinations of `Parameters` in a `Matrix`. This change implements the fan out of `TaskRuns` from a `PipelineTask` with a `Matrix`. The fanned-out `TaskRuns` are executed in parallel. [tep-0090]: https://github.com/tektoncd/community/blob/main/teps/0090-matrix.md
1 parent d48cfdd commit 4985acd

File tree

10 files changed

+494
-36
lines changed

10 files changed

+494
-36
lines changed

docs/matrix.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ weight: 11
1515
- [Results](#results)
1616
- [Specifying Results in a Matrix](#specifying-results-in-a-matrix)
1717
- [Results from fanned out PipelineTasks](#results-from-fanned-out-pipelinetasks)
18+
- [Fan Out](#fan-out)
19+
- [`PipelineTasks` with `Tasks`](#pipelinetasks-with-tasks)
1820

1921
## Overview
2022

@@ -134,3 +136,28 @@ Consuming `Results` from previous `TaskRuns` or `Runs` in a `Matrix`, which woul
134136
Consuming `Results` from fanned out `PipelineTasks` will not be in the supported in the initial iteration
135137
of `Matrix`. Supporting consuming `Results` from fanned out `PipelineTasks` will be revisited after array
136138
and object `Results` are supported.
139+
140+
## Fan Out
141+
142+
### `PipelineTasks` with `Tasks`
143+
144+
When a `PipelineTask` has a `Task` and a `Matrix`, the `Task` will be executed in parallel `TaskRuns` with
145+
substitutions from combinations of `Parameters`.
146+
147+
In this [example](/examples/v1beta1/pipelineruns/pipelinerun-with-matrix.yaml), nine `TaskRuns` are created
148+
with combinations of platforms ("linux", "mac", "windows") and browsers ("chrome", "safari", "firefox").
149+
150+
```shell
151+
$ tkn taskruns list
152+
153+
NAME STARTED DURATION STATUS
154+
matrixed-pr-6lvzk-platforms-and-browsers-8 11 seconds ago 7 seconds Succeeded
155+
matrixed-pr-6lvzk-platforms-and-browsers-6 12 seconds ago 7 seconds Succeeded
156+
matrixed-pr-6lvzk-platforms-and-browsers-7 12 seconds ago 9 seconds Succeeded
157+
matrixed-pr-6lvzk-platforms-and-browsers-4 12 seconds ago 7 seconds Succeeded
158+
matrixed-pr-6lvzk-platforms-and-browsers-5 12 seconds ago 6 seconds Succeeded
159+
matrixed-pr-6lvzk-platforms-and-browsers-3 13 seconds ago 7 seconds Succeeded
160+
matrixed-pr-6lvzk-platforms-and-browsers-1 13 seconds ago 8 seconds Succeeded
161+
matrixed-pr-6lvzk-platforms-and-browsers-2 13 seconds ago 8 seconds Succeeded
162+
matrixed-pr-6lvzk-platforms-and-browsers-0 13 seconds ago 8 seconds Succeeded
163+
```

docs/pipelineruns.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,17 +1012,19 @@ Task Runs:
10121012

10131013
The name of the `TaskRuns` and `Runs` owned by a `PipelineRun` are univocally associated to the owning resource.
10141014
If a `PipelineRun` resource is deleted and created with the same name, the child `TaskRuns` will be created with the
1015-
same name as before. The base format of the name is `<pipelinerun-name>-<pipelinetask-name>`. The name may vary
1016-
according the logic of [`kmeta.ChildName`](https://pkg.go.dev/github.com/knative/pkg/kmeta#ChildName).
1015+
same name as before. The base format of the name is `<pipelinerun-name>-<pipelinetask-name>`. If the `PipelineTask`
1016+
has a `Matrix`, the name will have an int suffix with format `<pipelinerun-name>-<pipelinetask-name>-<combination-id>`.
1017+
The name may vary according the logic of [`kmeta.ChildName`](https://pkg.go.dev/github.com/knative/pkg/kmeta#ChildName).
10171018

10181019
Some examples:
10191020

1020-
| `PipelineRun` Name | `PipelineTask` Name | `TaskRun` Name |
1021-
|--------------------------|------------------------------|--------------------|
1022-
| pipeline-run | task1 | pipeline-run-task1 |
1023-
| pipeline-run | task2-0123456789-0123456789-0123456789-0123456789-0123456789 | pipeline-runee4a397d6eab67777d4e6f9991cd19e6-task2-0123456789-0 |
1024-
| pipeline-run-0123456789-0123456789-0123456789-0123456789 | task3 | pipeline-run-0123456789-0123456789-0123456789-0123456789-task3 |
1025-
| pipeline-run-0123456789-0123456789-0123456789-0123456789 | task2-0123456789-0123456789-0123456789-0123456789-0123456789 | pipeline-run-0123456789-012345607ad8c7aac5873cdfabe472a68996b5c |
1021+
| `PipelineRun` Name | `PipelineTask` Name | `TaskRun` Names |
1022+
|----------------------------------------------------------|--------------------------------------------------------------|----------------------------------------------------------------------------------------|
1023+
| pipeline-run | task1 | pipeline-run-task1 |
1024+
| pipeline-run | task2-0123456789-0123456789-0123456789-0123456789-0123456789 | pipeline-runee4a397d6eab67777d4e6f9991cd19e6-task2-0123456789-0 |
1025+
| pipeline-run-0123456789-0123456789-0123456789-0123456789 | task3 | pipeline-run-0123456789-0123456789-0123456789-0123456789-task3 |
1026+
| pipeline-run-0123456789-0123456789-0123456789-0123456789 | task2-0123456789-0123456789-0123456789-0123456789-0123456789 | pipeline-run-0123456789-012345607ad8c7aac5873cdfabe472a68996b5c |
1027+
| pipeline-run | task4 (with 2x2 `Matrix`) | pipeline-run-task1-0, pipeline-run-task1-2, pipeline-run-task1-3, pipeline-run-task1-4 |
10261028

10271029
## Cancelling a `PipelineRun`
10281030

docs/pipelines.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ spec:
366366
- firefox
367367
```
368368

369-
For further information, read [`Matrix`](./matrix.md).
369+
For further information, read [`Matrix`](./matrix.md) and see [end-to-end example](/examples/v1beta1/pipelineruns/pipelinerun-with-matrix.yaml).
370370

371371
### Specifying `Workspaces` in `PipelineTasks`
372372

@@ -1199,7 +1199,7 @@ spec:
11991199
- "bar"
12001200
```
12011201

1202-
For further information, read [`Matrix`](./matrix.md).
1202+
For further information, read [`Matrix`](./matrix.md) and see [end-to-end example](/examples/v1beta1/pipelineruns/pipelinerun-with-matrix.yaml).
12031203

12041204
### Consuming `Task` execution results in `finally`
12051205

@@ -1640,7 +1640,7 @@ spec:
16401640
- thud
16411641
```
16421642

1643-
For further information, read [`Matrix`](./matrix.md).
1643+
For further information, read [`Matrix`](./matrix.md) and see [end-to-end example](/examples/v1beta1/pipelineruns/pipelinerun-with-matrix.yaml).
16441644

16451645
### Specifying workspaces
16461646

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: Task
3+
metadata:
4+
name: platform-browsers
5+
annotations:
6+
description: |
7+
A task that does something cool with platforms and browsers
8+
spec:
9+
params:
10+
- name: platform
11+
- name: browser
12+
steps:
13+
- name: echo
14+
image: alpine
15+
script: |
16+
echo "$(params.platform) and $(params.browser)"
17+
---
18+
# run platform-browsers task with:
19+
# platforms: linux, mac, windows
20+
# browsers: chrome, safari, firefox
21+
apiVersion: tekton.dev/v1beta1
22+
kind: PipelineRun
23+
metadata:
24+
generateName: matrixed-pr-
25+
spec:
26+
serviceAccountName: 'default'
27+
pipelineSpec:
28+
tasks:
29+
- name: platforms-and-browsers
30+
matrix:
31+
- name: platform
32+
value:
33+
- linux
34+
- mac
35+
- windows
36+
- name: browser
37+
value:
38+
- chrome
39+
- safari
40+
- firefox
41+
taskRef:
42+
name: platform-browsers

pkg/apis/pipeline/v1beta1/pipeline_types.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,15 +310,15 @@ func (pt *PipelineTask) validateMatrix(ctx context.Context) (errs *apis.FieldErr
310310
}
311311

312312
func (pt *PipelineTask) validateMatrixCombinationsCount(ctx context.Context) (errs *apis.FieldError) {
313-
matrixCombinationsCount := pt.getMatrixCombinationsCount()
313+
matrixCombinationsCount := pt.GetMatrixCombinationsCount()
314314
maxMatrixCombinationsCount := config.FromContextOrDefaults(ctx).Defaults.DefaultMaxMatrixCombinationsCount
315315
if matrixCombinationsCount > maxMatrixCombinationsCount {
316316
errs = errs.Also(apis.ErrOutOfBoundsValue(matrixCombinationsCount, 0, maxMatrixCombinationsCount, "matrix"))
317317
}
318318
return errs
319319
}
320320

321-
func (pt *PipelineTask) getMatrixCombinationsCount() int {
321+
func (pt *PipelineTask) GetMatrixCombinationsCount() int {
322322
if len(pt.Matrix) == 0 {
323323
return 0
324324
}

pkg/apis/pipeline/v1beta1/pipeline_types_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,7 +791,7 @@ func TestPipelineTask_validateMatrix(t *testing.T) {
791791
}
792792
}
793793

794-
func TestPipelineTask_getMatrixCombinationsCount(t *testing.T) {
794+
func TestPipelineTask_GetMatrixCombinationsCount(t *testing.T) {
795795
tests := []struct {
796796
name string
797797
pt *PipelineTask
@@ -860,8 +860,8 @@ func TestPipelineTask_getMatrixCombinationsCount(t *testing.T) {
860860
}}
861861
for _, tt := range tests {
862862
t.Run(tt.name, func(t *testing.T) {
863-
if d := cmp.Diff(tt.matrixCombinationsCount, tt.pt.getMatrixCombinationsCount()); d != "" {
864-
t.Errorf("PipelineTask.getMatrixCombinationsCount() errors diff %s", diff.PrintWantGot(d))
863+
if d := cmp.Diff(tt.matrixCombinationsCount, tt.pt.GetMatrixCombinationsCount()); d != "" {
864+
t.Errorf("PipelineTask.GetMatrixCombinationsCount() errors diff %s", diff.PrintWantGot(d))
865865
}
866866
})
867867
}

pkg/reconciler/pipelinerun/pipelinerun.go

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"path/filepath"
2525
"reflect"
26+
"strconv"
2627
"strings"
2728
"time"
2829

@@ -38,6 +39,7 @@ import (
3839
listersv1alpha1 "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1"
3940
listers "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1beta1"
4041
resourcelisters "github.com/tektoncd/pipeline/pkg/client/resource/listers/resource/v1alpha1"
42+
"github.com/tektoncd/pipeline/pkg/matrix"
4143
"github.com/tektoncd/pipeline/pkg/pipelinerunmetrics"
4244
tknreconciler "github.com/tektoncd/pipeline/pkg/reconciler"
4345
"github.com/tektoncd/pipeline/pkg/reconciler/events"
@@ -688,7 +690,8 @@ func (c *Reconciler) runNextSchedulableTask(ctx context.Context, pr *v1beta1.Pip
688690
if rprt == nil || rprt.Skip(pipelineRunFacts).IsSkipped || rprt.IsFinallySkipped(pipelineRunFacts).IsSkipped {
689691
continue
690692
}
691-
if rprt.IsCustomTask() {
693+
switch {
694+
case rprt.IsCustomTask():
692695
if rprt.IsFinalTask(pipelineRunFacts) {
693696
rprt.Run, err = c.createRun(ctx, rprt, pr, getFinallyTaskRunTimeout)
694697
} else {
@@ -698,16 +701,27 @@ func (c *Reconciler) runNextSchedulableTask(ctx context.Context, pr *v1beta1.Pip
698701
recorder.Eventf(pr, corev1.EventTypeWarning, "RunCreationFailed", "Failed to create Run %q: %v", rprt.RunName, err)
699702
return fmt.Errorf("error creating Run called %s for PipelineTask %s from PipelineRun %s: %w", rprt.RunName, rprt.PipelineTask.Name, pr.Name, err)
700703
}
701-
} else {
704+
case rprt.IsMatrixed():
705+
if rprt.IsFinalTask(pipelineRunFacts) {
706+
rprt.TaskRuns, err = c.createTaskRuns(ctx, rprt, pr, as.StorageBasePath(pr), getFinallyTaskRunTimeout)
707+
} else {
708+
rprt.TaskRuns, err = c.createTaskRuns(ctx, rprt, pr, as.StorageBasePath(pr), getTaskRunTimeout)
709+
}
710+
if err != nil {
711+
recorder.Eventf(pr, corev1.EventTypeWarning, "TaskRunsCreationFailed", "Failed to create TaskRuns %q: %v", rprt.TaskRunNames, err)
712+
return fmt.Errorf("error creating TaskRuns called %s for PipelineTask %s from PipelineRun %s: %w", rprt.TaskRunNames, rprt.PipelineTask.Name, pr.Name, err)
713+
}
714+
default:
702715
if rprt.IsFinalTask(pipelineRunFacts) {
703-
rprt.TaskRun, err = c.createTaskRun(ctx, rprt, pr, as.StorageBasePath(pr), getFinallyTaskRunTimeout)
716+
rprt.TaskRun, err = c.createTaskRun(ctx, rprt.TaskRunName, nil, rprt, pr, as.StorageBasePath(pr), getFinallyTaskRunTimeout)
704717
} else {
705-
rprt.TaskRun, err = c.createTaskRun(ctx, rprt, pr, as.StorageBasePath(pr), getTaskRunTimeout)
718+
rprt.TaskRun, err = c.createTaskRun(ctx, rprt.TaskRunName, nil, rprt, pr, as.StorageBasePath(pr), getTaskRunTimeout)
706719
}
707720
if err != nil {
708721
recorder.Eventf(pr, corev1.EventTypeWarning, "TaskRunCreationFailed", "Failed to create TaskRun %q: %v", rprt.TaskRunName, err)
709722
return fmt.Errorf("error creating TaskRun called %s for PipelineTask %s from PipelineRun %s: %w", rprt.TaskRunName, rprt.PipelineTask.Name, pr.Name, err)
710723
}
724+
711725
}
712726
}
713727
return nil
@@ -750,10 +764,24 @@ func (c *Reconciler) updateRunsStatusDirectly(pr *v1beta1.PipelineRun) error {
750764

751765
type getTimeoutFunc func(ctx context.Context, pr *v1beta1.PipelineRun, rprt *resources.ResolvedPipelineRunTask, c clock.PassiveClock) *metav1.Duration
752766

753-
func (c *Reconciler) createTaskRun(ctx context.Context, rprt *resources.ResolvedPipelineRunTask, pr *v1beta1.PipelineRun, storageBasePath string, getTimeoutFunc getTimeoutFunc) (*v1beta1.TaskRun, error) {
767+
func (c *Reconciler) createTaskRuns(ctx context.Context, rprt *resources.ResolvedPipelineRunTask, pr *v1beta1.PipelineRun, storageBasePath string, getTimeoutFunc getTimeoutFunc) ([]*v1beta1.TaskRun, error) {
768+
var taskRuns []*v1beta1.TaskRun
769+
matrixCombinations := matrix.FanOut(rprt.PipelineTask.Matrix).ToMap()
770+
for i, taskRunName := range rprt.TaskRunNames {
771+
params := matrixCombinations[strconv.Itoa(i)]
772+
taskRun, err := c.createTaskRun(ctx, taskRunName, params, rprt, pr, storageBasePath, getTimeoutFunc)
773+
if err != nil {
774+
return nil, err
775+
}
776+
taskRuns = append(taskRuns, taskRun)
777+
}
778+
return taskRuns, nil
779+
}
780+
781+
func (c *Reconciler) createTaskRun(ctx context.Context, taskRunName string, params []v1beta1.Param, rprt *resources.ResolvedPipelineRunTask, pr *v1beta1.PipelineRun, storageBasePath string, getTimeoutFunc getTimeoutFunc) (*v1beta1.TaskRun, error) {
754782
logger := logging.FromContext(ctx)
755783

756-
tr, _ := c.taskRunLister.TaskRuns(pr.Namespace).Get(rprt.TaskRunName)
784+
tr, _ := c.taskRunLister.TaskRuns(pr.Namespace).Get(taskRunName)
757785
if tr != nil {
758786
// Don't modify the lister cache's copy.
759787
tr = tr.DeepCopy()
@@ -767,16 +795,19 @@ func (c *Reconciler) createTaskRun(ctx context.Context, rprt *resources.Resolved
767795

768796
rprt.PipelineTask = resources.ApplyPipelineTaskContexts(rprt.PipelineTask)
769797
taskRunSpec := pr.GetTaskRunSpec(rprt.PipelineTask.Name)
798+
if len(params) == 0 {
799+
params = rprt.PipelineTask.Params
800+
}
770801
tr = &v1beta1.TaskRun{
771802
ObjectMeta: metav1.ObjectMeta{
772-
Name: rprt.TaskRunName,
803+
Name: taskRunName,
773804
Namespace: pr.Namespace,
774805
OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(pr)},
775806
Labels: combineTaskRunAndTaskSpecLabels(pr, rprt.PipelineTask),
776807
Annotations: combineTaskRunAndTaskSpecAnnotations(pr, rprt.PipelineTask),
777808
},
778809
Spec: v1beta1.TaskRunSpec{
779-
Params: rprt.PipelineTask.Params,
810+
Params: params,
780811
ServiceAccountName: taskRunSpec.TaskServiceAccountName,
781812
Timeout: getTimeoutFunc(ctx, pr, rprt, c.Clock),
782813
PodTemplate: taskRunSpec.TaskPodTemplate,
@@ -803,7 +834,7 @@ func (c *Reconciler) createTaskRun(ctx context.Context, rprt *resources.Resolved
803834
}
804835

805836
resources.WrapSteps(&tr.Spec, rprt.PipelineTask, rprt.ResolvedTaskResources.Inputs, rprt.ResolvedTaskResources.Outputs, storageBasePath)
806-
logger.Infof("Creating a new TaskRun object %s for pipeline task %s", rprt.TaskRunName, rprt.PipelineTask.Name)
837+
logger.Infof("Creating a new TaskRun object %s for pipeline task %s", taskRunName, rprt.PipelineTask.Name)
807838
return c.PipelineClientSet.TektonV1beta1().TaskRuns(pr.Namespace).Create(ctx, tr, metav1.CreateOptions{})
808839
}
809840

0 commit comments

Comments
 (0)