Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TEP-0090: Fan Out Runs #5049

Merged
merged 1 commit into from
Jun 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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