Skip to content

Commit

Permalink
TEP-0090: Fan Out Runs
Browse files Browse the repository at this point in the history
[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 `Runs` from a `PipelineTask`
with a `Matrix`. The fanned-out `Runs` are executed in parallel.

[tep-0090]: https://github.com/tektoncd/community/blob/main/teps/0090-matrix.md
  • Loading branch information
jerop authored and tekton-robot committed Jun 29, 2022
1 parent 8611c55 commit 8a3a8e5
Show file tree
Hide file tree
Showing 7 changed files with 839 additions and 12 deletions.
150 changes: 150 additions & 0 deletions docs/matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ weight: 11
- [Results from fanned out PipelineTasks](#results-from-fanned-out-pipelinetasks)
- [Fan Out](#fan-out)
- [`PipelineTasks` with `Tasks`](#pipelinetasks-with-tasks)
- [`PipelineTasks` with `Custom Tasks`](#pipelinetasks-with-custom-tasks)

## Overview

Expand Down Expand Up @@ -307,4 +308,153 @@ status:

To execute this example yourself, run [`PipelineRun` with `Matrix`][pr-with-matrix].

### `PipelineTasks` with `Custom Tasks`

When a `PipelineTask` has a `Custom Task` and a `Matrix`, the `Custom Task` will be executed in parallel `Runs` with
substitutions from combinations of `Parameters`.

In the example below, eight `Runs` are created with combinations of CEL expressions, using the [CEL `Custom Task`][cel].

```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: matrixed-pr-
spec:
serviceAccountName: 'default'
pipelineSpec:
tasks:
- name: platforms-and-browsers
matrix:
- name: type
value:
- "type(1)"
- "type(1.0)"
- name: colors
value:
- "{'blue': '0x000080', 'red': '0xFF0000'}['blue']"
- "{'blue': '0x000080', 'red': '0xFF0000'}['red']"
- name: bool
value:
- "type(1) == int"
- "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'"
taskRef:
apiVersion: cel.tekton.dev/v1alpha1
kind: CEL
```

When the above `PipelineRun` is executed, these `Runs` are created:

```shell
$ k get run.tekton.dev
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
matrixed-pr-4djw9-platforms-and-browsers-0 True EvaluationSuccess 10s 10s
matrixed-pr-4djw9-platforms-and-browsers-1 True EvaluationSuccess 10s 10s
matrixed-pr-4djw9-platforms-and-browsers-2 True EvaluationSuccess 10s 10s
matrixed-pr-4djw9-platforms-and-browsers-3 True EvaluationSuccess 9s 9s
matrixed-pr-4djw9-platforms-and-browsers-4 True EvaluationSuccess 9s 9s
matrixed-pr-4djw9-platforms-and-browsers-5 True EvaluationSuccess 9s 9s
matrixed-pr-4djw9-platforms-and-browsers-6 True EvaluationSuccess 9s 9s
matrixed-pr-4djw9-platforms-and-browsers-7 True EvaluationSuccess 9s 9s
```

When the above `PipelineRun` is executed, its status is populated with `ChildReferences` of the above `Runs`. The
`PipelineRun` status tracks the status of all the fanned out `Runs`. This is the `PipelineRun` after completing:

```yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: matrixed-pr-
labels:
tekton.dev/pipeline: matrixed-pr-4djw9
name: matrixed-pr-4djw9
namespace: default
spec:
pipelineSpec:
tasks:
- matrix:
- name: type
value:
- type(1)
- type(1.0)
- name: colors
value:
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''blue'']'
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''red'']'
- name: bool
value:
- type(1) == int
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''red''] == ''0xFF0000'''
name: platforms-and-browsers
taskRef:
apiVersion: cel.tekton.dev/v1alpha1
kind: CEL
serviceAccountName: default
timeout: 1h0m0s
status:
pipelineSpec:
tasks:
- matrix:
- name: type
value:
- type(1)
- type(1.0)
- name: colors
value:
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''blue'']'
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''red'']'
- name: bool
value:
- type(1) == int
- '{''blue'': ''0x000080'', ''red'': ''0xFF0000''}[''red''] == ''0xFF0000'''
name: platforms-and-browsers
taskRef:
apiVersion: cel.tekton.dev/v1alpha1
kind: CEL
startTime: "2022-06-28T20:49:40Z"
completionTime: "2022-06-28T20:49:41Z"
conditions:
- lastTransitionTime: "2022-06-28T20:49:41Z"
message: 'Tasks Completed: 1 (Failed: 0, Cancelled 0), Skipped: 0'
reason: Succeeded
status: "True"
type: Succeeded
childReferences:
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-1
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-2
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-3
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-4
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-5
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-6
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-7
pipelineTaskName: platforms-and-browsers
- apiVersion: tekton.dev/v1alpha1
kind: Run
name: matrixed-pr-4djw9-platforms-and-browsers-0
pipelineTaskName: platforms-and-browsers
```

[cel]: https://github.com/tektoncd/experimental/tree/1609827ea81d05c8d00f8933c5c9d6150cd36989/cel
[pr-with-matrix]: ../examples/v1beta1/pipelineruns/alpha/pipelinerun-with-matrix.yaml
37 changes: 31 additions & 6 deletions pkg/reconciler/pipelinerun/pipelinerun.go
Original file line number Diff line number Diff line change
Expand Up @@ -689,11 +689,21 @@ func (c *Reconciler) runNextSchedulableTask(ctx context.Context, pr *v1beta1.Pip
continue
}
switch {
case rpt.IsCustomTask() && rpt.IsMatrixed():
if rpt.IsFinalTask(pipelineRunFacts) {
rpt.Runs, err = c.createRuns(ctx, rpt, pr, getFinallyTaskRunTimeout)
} else {
rpt.Runs, err = c.createRuns(ctx, rpt, pr, getTaskRunTimeout)
}
if err != nil {
recorder.Eventf(pr, corev1.EventTypeWarning, "RunsCreationFailed", "Failed to create Runs %q: %v", rpt.RunNames, err)
return fmt.Errorf("error creating Runs called %s for PipelineTask %s from PipelineRun %s: %w", rpt.RunNames, rpt.PipelineTask.Name, pr.Name, err)
}
case rpt.IsCustomTask():
if rpt.IsFinalTask(pipelineRunFacts) {
rpt.Run, err = c.createRun(ctx, rpt, pr, getFinallyTaskRunTimeout)
rpt.Run, err = c.createRun(ctx, rpt.RunName, nil, rpt, pr, getFinallyTaskRunTimeout)
} else {
rpt.Run, err = c.createRun(ctx, rpt, pr, getTaskRunTimeout)
rpt.Run, err = c.createRun(ctx, rpt.RunName, nil, rpt, pr, getTaskRunTimeout)
}
if err != nil {
recorder.Eventf(pr, corev1.EventTypeWarning, "RunCreationFailed", "Failed to create Run %q: %v", rpt.RunName, err)
Expand Down Expand Up @@ -836,12 +846,27 @@ func (c *Reconciler) createTaskRun(ctx context.Context, taskRunName string, para
return c.PipelineClientSet.TektonV1beta1().TaskRuns(pr.Namespace).Create(ctx, tr, metav1.CreateOptions{})
}

func (c *Reconciler) createRun(ctx context.Context, rpt *resources.ResolvedPipelineTask, pr *v1beta1.PipelineRun, getTimeoutFunc getTimeoutFunc) (*v1alpha1.Run, error) {
func (c *Reconciler) createRuns(ctx context.Context, rpt *resources.ResolvedPipelineTask, pr *v1beta1.PipelineRun, getTimeoutFunc getTimeoutFunc) ([]*v1alpha1.Run, error) {
var runs []*v1alpha1.Run
matrixCombinations := matrix.FanOut(rpt.PipelineTask.Matrix).ToMap()
for i, runName := range rpt.RunNames {
params := matrixCombinations[strconv.Itoa(i)]
run, err := c.createRun(ctx, runName, params, rpt, pr, getTimeoutFunc)
if err != nil {
return nil, err
}
runs = append(runs, run)
}
return runs, nil
}

func (c *Reconciler) createRun(ctx context.Context, runName string, params []v1beta1.Param, rpt *resources.ResolvedPipelineTask, pr *v1beta1.PipelineRun, getTimeoutFunc getTimeoutFunc) (*v1alpha1.Run, error) {
logger := logging.FromContext(ctx)
taskRunSpec := pr.GetTaskRunSpec(rpt.PipelineTask.Name)
params = append(params, rpt.PipelineTask.Params...)
r := &v1alpha1.Run{
ObjectMeta: metav1.ObjectMeta{
Name: rpt.RunName,
Name: runName,
Namespace: pr.Namespace,
OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(pr)},
Labels: getTaskrunLabels(pr, rpt.PipelineTask.Name, true),
Expand All @@ -850,7 +875,7 @@ func (c *Reconciler) createRun(ctx context.Context, rpt *resources.ResolvedPipel
Spec: v1alpha1.RunSpec{
Retries: rpt.PipelineTask.Retries,
Ref: rpt.PipelineTask.TaskRef,
Params: rpt.PipelineTask.Params,
Params: params,
ServiceAccountName: taskRunSpec.TaskServiceAccountName,
Timeout: getTimeoutFunc(ctx, pr, rpt, c.Clock),
PodTemplate: taskRunSpec.TaskPodTemplate,
Expand Down Expand Up @@ -886,7 +911,7 @@ func (c *Reconciler) createRun(ctx context.Context, rpt *resources.ResolvedPipel
r.Annotations[workspace.AnnotationAffinityAssistantName] = getAffinityAssistantName(pipelinePVCWorkspaceName, pr.Name)
}

logger.Infof("Creating a new Run object %s", rpt.RunName)
logger.Infof("Creating a new Run object %s", runName)
return c.PipelineClientSet.TektonV1alpha1().Runs(pr.Namespace).Create(ctx, r, metav1.CreateOptions{})
}

Expand Down
Loading

0 comments on commit 8a3a8e5

Please sign in to comment.